diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-12-16 06:06:25 +0000 |
commit | aa944aa3453e47706685bc562711a9e87375941e (patch) | |
tree | 8fb37a65f205a90412917ca2b91c429263ef1790 /drivers | |
parent | 967c65a99059fd459b956c1588ce0ba227912c4e (diff) |
Merge with Linux 2.1.72, part 2.
The new signal code with exception of the code for the rt signals.
The definitions in <asm/siginfo.h> and <asm/ucontext.h> are currently
just stolen from the Alpha and will need to be overhauled.
Diffstat (limited to 'drivers')
47 files changed, 8397 insertions, 0 deletions
diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c new file mode 100644 index 000000000..6a2886f78 --- /dev/null +++ b/drivers/block/ide-dma.c @@ -0,0 +1,678 @@ +/* + * linux/drivers/block/ide-dma.c Version 4.06 December 3, 1997 + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA functions + * of various PCI chipsets, including the Intel PIIX (i82371FB for + * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and + * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset) + * ("PIIX" stands for "PCI ISA IDE Xcellerator"). + * + * Pretty much the same code works for other IDE PCI bus-mastering chipsets. + * + * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies). + * + * By default, DMA support is prepared for use, but is currently enabled only + * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single), + * or which are recognized as "good" (see table below). Drives with only mode0 + * or mode1 (multi/single) DMA should also work with this chipset/driver + * (eg. MC2112A) but are not enabled by default. + * + * Use "hdparm -i" to view modes supported by a given drive. + * + * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling + * DMA support, but must be (re-)compiled against this kernel version or later. + * + * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting. + * If problems arise, ide.c will disable DMA operation after a few retries. + * This error recovery mechanism works and has been extremely well exercised. + * + * IDE drives, depending on their vintage, may support several different modes + * of DMA operation. The boot-time modes are indicated with a "*" in + * the "hdparm -i" listing, and can be changed with *knowledgeable* use of + * the "hdparm -X" feature. There is seldom a need to do this, as drives + * normally power-up with their "best" PIO/DMA modes enabled. + * + * Testing has been done with a rather extensive number of drives, + * with Quantum & Western Digital models generally outperforming the pack, + * and Fujitsu & Conner (and some Seagate which are really Conner) drives + * showing more lackluster throughput. + * + * Keep an eye on /var/adm/messages for "DMA disabled" messages. + * + * Some people have reported trouble with Intel Zappa motherboards. + * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0, + * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe + * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this). + * + * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for fixing the + * problem with some (all?) ACER motherboards/BIOSs. Hopefully the fix + * still works here (?). + * + * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing + * "TX" chipset compatibility and for providing patches for the "TX" chipset. + * + * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack + * at generic DMA -- his patches were referred to when preparing this code. + * + * Most importantly, thanks to Robert Bringman <rob@mars.trion.com> + * for supplying a Promise UDMA board & WD UDMA drive for this work! + * + * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/dma.h> + +#include "ide.h" + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mode2 DMA but which are + * known to work fine with this interface under Linux. + */ +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + NULL}; + +/* + * Our Physical Region Descriptor (PRD) table should be large enough + * to handle the biggest I/O request we are likely to see. Since requests + * can have no more than 256 sectors, and since the typical blocksize is + * two or more sectors, we could get by with a limit of 128 entries here for + * the usual worst case. Most requests seem to include some contiguous blocks, + * further reducing the number of table entries required. + * + * The driver reverts to PIO mode for individual requests that exceed + * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling + * 100% of all crazy scenarios here is not necessary. + * + * As it turns out though, we must allocate a full 4KB page for this, + * so the two PRD tables (ide0 & ide1) will each get half of that, + * allowing each to have about 256 entries (8 bytes each) from this. + */ +#define PRD_BYTES 8 +#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) + +static int config_drive_for_dma (ide_drive_t *); + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +static void dma_intr (ide_drive_t *drive) +{ + byte stat, dma_stat; + int i; + struct request *rq = HWGROUP(drive)->rq; + unsigned short dma_base = HWIF(drive)->dma_base; + + dma_stat = inb(dma_base+2); /* get DMA status */ + outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */ + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if ((dma_stat & 7) == 4) { /* verify good DMA status */ + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return; + } + printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat); + } + sti(); + ide_error(drive, "dma_intr", stat); +} + +/* + * ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + * May also be invoked from trm290.c + */ +int ide_build_dmatable (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long size, addr, *table = HWIF(drive)->dmatable; +#ifdef CONFIG_BLK_DEV_TRM290 + unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290); +#else + const int is_trm290_chipset = 0; +#endif + unsigned int count = 0; + + do { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + */ + if (bh == NULL) { /* paging requests have (rq->bh == NULL) */ + addr = virt_to_bus (rq->buffer); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + addr = virt_to_bus (bh->b_data); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus (bh->b_data)) + break; + size += bh->b_size; + } + } + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * The hardware requires 16-bit alignment of all blocks + * (trm290 requires 32-bit alignment). + */ + if ((addr & 3)) { + printk("%s: misaligned DMA buffer\n", drive->name); + return 0; + } + while (size) { + if (++count >= PRD_ENTRIES) { + printk("%s: DMA table too small\n", drive->name); + return 0; /* revert to PIO for this request */ + } else { + unsigned long xcount, bcount = 0x10000 - (addr & 0xffff); + if (bcount > size) + bcount = size; + *table++ = addr; + xcount = bcount & 0xffff; + if (is_trm290_chipset) + xcount = ((xcount >> 2) - 1) << 16; + *table++ = xcount; + addr += bcount; + size -= bcount; + } + } + } while (bh != NULL); + if (count) { + if (!is_trm290_chipset) + *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */ + return count; + } + printk("%s: empty DMA table?\n", drive->name); + return 0; +} + +/* + * ide_dmaproc() initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + * May also be invoked from trm290.c + */ +int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + unsigned int count, reading = 0; + + switch (func) { + case ide_dma_off: + printk("%s: DMA disabled\n", drive->name); + case ide_dma_off_quietly: + case ide_dma_on: + drive->using_dma = (func == ide_dma_on); + return 0; + case ide_dma_abort: + outb(inb(dma_base)&~1, dma_base); /* stop DMA */ + return 0; + case ide_dma_check: + return config_drive_for_dma (drive); + case ide_dma_status_bad: + return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */ + case ide_dma_transferred: + return 0; /* NOT IMPLEMENTED: number of bytes actually transferred */ + case ide_dma_begin: + outb(inb(dma_base)|1, dma_base); /* begin DMA */ + return 0; + default: + printk("ide_dmaproc: unsupported func: %d\n", func); + return 1; + case ide_dma_read: + reading = 1 << 3; + case ide_dma_write: + if (!(count = ide_build_dmatable(drive))) + return 1; /* try PIO instead of DMA */ + outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */ + outb(reading, dma_base); /* specify r/w */ + outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */ + if (drive->media != ide_disk) + return 0; + ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */ + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + outb(inb(dma_base)|1, dma_base); /* begin DMA */ + return 0; + } +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id && (id->capability & 1)) { + /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */ + if (id->field_valid & 4) /* UltraDMA */ + if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) + return hwif->dmaproc(ide_dma_on, drive); + /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) + return hwif->dmaproc(ide_dma_on, drive); + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) + return hwif->dmaproc(ide_dma_on, drive); + } + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports) +{ + static unsigned long dmatable = 0; + static unsigned leftover = 0; + + printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase + num_ports - 1); + if (check_region(dmabase, num_ports)) { + printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n"); + return; + } + request_region(dmabase, num_ports, hwif->name); + hwif->dma_base = dmabase; + if (leftover < (PRD_ENTRIES * PRD_BYTES)) { + /* + * The BM-DMA uses full 32bit addr, so we can + * safely use __get_free_page() here instead + * of __get_dma_pages() -- no ISA limitations. + */ + dmatable = __get_free_pages(GFP_KERNEL,1,0); + leftover = dmatable ? PAGE_SIZE : 0; + } + if (!dmatable) { + printk(" -- ERROR, UNABLE TO ALLOCATE PRD TABLE\n"); + } else { + hwif->dmatable = (unsigned long *) dmatable; + dmatable += (PRD_ENTRIES * PRD_BYTES); + leftover -= (PRD_ENTRIES * PRD_BYTES); + hwif->dmaproc = &ide_dmaproc; + if (hwif->chipset != ide_trm290) { + byte dma_stat = inb(dmabase+2); + printk(", BIOS DMA settings: %s:%s %s:%s", + hwif->drives[0].name, (dma_stat & 0x20) ? "yes" : "no ", + hwif->drives[1].name, (dma_stat & 0x40) ? "yes" : "no"); + } + printk("\n"); + } +} + +#ifdef CONFIG_BLK_DEV_TRM290 +extern void ide_init_trm290(byte, byte, ide_hwif_t *); +#define INIT_TRM290 (&ide_init_trm290) +#else +#define INIT_TRM290 (NULL) +#endif /* CONFIG_BLK_DEV_TRM290 */ + +#ifdef CONFIG_BLK_DEV_OPTI621 +extern void ide_init_opti621(byte, byte, ide_hwif_t *); +#define INIT_OPTI (&ide_init_opti621) +#else +#define INIT_OPTI (NULL) +#endif /* CONFIG_BLK_DEV_OPTI621 */ + +#define DEVID_PIIX (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371_1 <<16)) +#define DEVID_PIIX3 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371SB_1 <<16)) +#define DEVID_PIIX4 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371AB <<16)) +#define DEVID_VP_IDE (PCI_VENDOR_ID_VIA |(PCI_DEVICE_ID_VIA_82C586_1 <<16)) +#define DEVID_PDC20246 (PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246 <<16)) +#define DEVID_RZ1000 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1000 <<16)) +#define DEVID_RZ1001 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1001 <<16)) +#define DEVID_CMD640 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_640 <<16)) +#define DEVID_CMD646 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_646 <<16)) +#define DEVID_SIS5513 (PCI_VENDOR_ID_SI |(PCI_DEVICE_ID_SI_5513 <<16)) +#define DEVID_OPTI (PCI_VENDOR_ID_OPTI |(PCI_DEVICE_ID_OPTI_82C621 <<16)) +#define DEVID_OPTI2 (PCI_VENDOR_ID_OPTI |(0xd568 /* from datasheets */ <<16)) +#define DEVID_TRM290 (PCI_VENDOR_ID_TEKRAM |(PCI_DEVICE_ID_TEKRAM_DC290 <<16)) +#define DEVID_NS87410 (PCI_VENDOR_ID_NS |(PCI_DEVICE_ID_NS_87410 <<16)) +#define DEVID_HT6565 (PCI_VENDOR_ID_HOLTEK |(PCI_DEVICE_ID_HOLTEK_6565 <<16)) + +typedef struct ide_pci_enablebit_s { + byte reg; /* byte pci reg holding the enable-bit */ + byte mask; /* mask to isolate the enable-bit */ + byte val; /* value of masked reg when "enabled" */ +} ide_pci_enablebit_t; + +typedef struct ide_pci_device_s { + unsigned int id; + const char *name; + void (*init_hwif)(byte bus, byte fn, ide_hwif_t *hwif); + ide_pci_enablebit_t enablebits[2]; +} ide_pci_device_t; + +static ide_pci_device_t ide_pci_chipsets[] = { + {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} }, + {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} }, + {DEVID_RZ1000, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_RZ1001, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_CMD640, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_OPTI, "OPTI", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_OPTI2, "OPTI2", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} }, + {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} }, + {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} }, + {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {0, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }}; + +__initfunc(static ide_pci_device_t *lookup_devid(unsigned int devid)) +{ + ide_pci_device_t *d = ide_pci_chipsets; + while (d->id && d->id != devid) + ++d; + return d; +} + +/* The next two functions were stolen from cmd640.c, with + a few modifications */ + +__initfunc(static void write_pcicfg_dword (byte fn, unsigned short reg, long val)) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + outl_p(val, (reg & 3) | 0xcfc); + restore_flags(flags); +} + +__initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg)) +{ + long b; + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + b = inl_p((reg & 3) | 0xcfc); + restore_flags(flags); + return b; +} + +/* + * Search for an (apparently) unused block of I/O space + * of "size" bytes in length. + */ +__initfunc(static short find_free_region (unsigned short size)) +{ + unsigned short i, base = 0xe800; + for (base = 0xe800; base > 0; base -= 0x800) { + if (!check_region(base,size)) { + for (i = 0; i < size; i++) { + if (inb(base+i) != 0xff) + goto next; + } + return base; /* success */ + } + next: + } + return 0; /* failure */ +} + +/* + * Fetch the Bus-Master I/O Base-Address (BMIBA) from PCI space: + */ +__initfunc(static unsigned int ide_get_or_set_bmiba (byte bus, byte fn, const char *name)) +{ + unsigned int bmiba = 0; + unsigned short base; + int rc; + + if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) { + printk("%s: failed to read BMIBA\n", name); + } else if ((bmiba &= 0xfff0) == 0) { + printk("%s: BMIBA is invalid (0x%04x, BIOS problem)\n", name, bmiba); + base = find_free_region(16); + if (base) { + printk("%s: setting BMIBA to 0x%04x\n", name, base); + pcibios_write_config_dword(bus, fn, 0x20, base | 1); + pcibios_read_config_dword(bus, fn, 0x20, &bmiba); + bmiba &= 0xfff0; + if (bmiba != base) { + if (bus == 0) { + printk("%s: operation failed, bypassing BIOS to try again\n", name); + write_pcicfg_dword(fn, 0x20, base | 1); + bmiba = read_pcicfg_dword(fn, 0x20) & 0xfff0; + } + if (bmiba != base) { + printk("%s: operation failed, DMA disabled\n", name); + bmiba = 0; + } + } + } + } + return bmiba; +} + +/* + * Match a PCI IDE port against an entry in ide_hwifs[], + * based on io_base port if possible. + */ +__initfunc(ide_hwif_t *ide_match_hwif (unsigned int io_base)) +{ + int h; + ide_hwif_t *hwif; + + /* + * Look for a hwif with matching io_base specified using + * parameters to ide_setup(). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_generic) + return hwif; /* a perfect match */ + } + } + /* + * Look for a hwif with matching io_base default value. + * If chipset is "ide_unknown", then claim that hwif slot. + * Otherwise, some other chipset has already claimed it.. :( + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_unknown) + return hwif; /* match */ + return NULL; /* already claimed */ + } + } + /* + * Okay, there is no hwif matching our io_base, + * so we'll just claim an unassigned slot. + * Give preference to claiming ide2/ide3 before ide0/ide1, + * just in case there's another interface yet-to-be-scanned + * which uses ports 1f0/170 (the ide0/ide1 defaults). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */ + hwif = &ide_hwifs[hwifs[h]]; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + return NULL; +} + +/* + * ide_setup_pci_device() looks at the primary/secondary interfaces + * on a PCI IDE device and, if they are enabled, prepares the IDE driver + * for use with them. This generic code works for most PCI chipsets. + * + * One thing that is not standardized is the location of the + * primary/secondary interface "enable/disable" bits. For chipsets that + * we "know" about, this information is in the ide_pci_device_t struct; + * for all other chipsets, we just assume both interfaces are enabled. + */ +__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int bmiba, ide_pci_device_t *d)) +{ + unsigned int port, at_least_one_hwif_enabled = 0; + unsigned short base = 0, ctl = 0; + byte tmp = 0; + ide_hwif_t *hwif, *mate = NULL; + + for (port = 0; port <= 1; ++port) { + ide_pci_enablebit_t *e = &(d->enablebits[port]); + if (e->reg) { + if (pcibios_read_config_byte(bus, fn, e->reg, &tmp)) { + printk("%s: unable to read pci reg 0x%x\n", d->name, e->reg); + } else if ((tmp & e->mask) != e->val) + continue; /* port not enabled */ + } + if (pcibios_read_config_word(bus, fn, 0x14+(port*8), &ctl)) + ctl = 0; + if ((ctl &= 0xfffc) == 0) + ctl = 0x3f4 ^ (port << 7); + if (pcibios_read_config_word(bus, fn, 0x10+(port*8), &base)) + base = 0; + if ((base &= 0xfff8) == 0) + base = 0x1F0 ^ (port << 7); + if ((hwif = ide_match_hwif(base)) == NULL) { + printk("%s: no room in hwif table for port %d\n", d->name, port); + continue; + } + hwif->chipset = ide_pci; + hwif->pci_port = port; + if (mate) { + hwif->mate = mate; + mate->mate = hwif; + } + mate = hwif; /* for next iteration */ + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + ide_init_hwif_ports(hwif->io_ports, base, NULL); + hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2; + } + if (bmiba) { + if ((inb(bmiba+2) & 0x80)) { /* simplex DMA only? */ + printk("%s: simplex device: DMA disabled\n", d->name); + } else { /* supports simultaneous DMA on both channels */ + ide_setup_dma(hwif, bmiba + (8 * port), 8); + } + } + if (d->id) { /* For "known" chipsets, allow other irqs during i/o */ + hwif->drives[0].unmask = 1; + hwif->drives[1].unmask = 1; + } + if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ + d->init_hwif(bus, fn, hwif); + at_least_one_hwif_enabled = 1; + } + if (!at_least_one_hwif_enabled) + printk("%s: neither IDE port enabled (BIOS)\n", d->name); +} + +/* + * ide_scan_pci_device() examines all functions of a PCI device, + * looking for IDE interfaces and/or devices in ide_pci_chipsets[]. + */ +__initfunc(static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn)) +{ + unsigned int devid, ccode; + unsigned short pcicmd, class; + ide_pci_device_t *d; + byte hedt, progif; + + if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt)) + hedt = 0; + do { + if (pcibios_read_config_dword(bus, fn, 0x00, &devid) + || devid == 0xffffffff + || pcibios_read_config_dword(bus, fn, 0x08, &ccode)) + return; + d = lookup_devid(devid); + if (d->name == NULL) /* some chips (cmd640, rz1000) are handled elsewhere */ + continue; + progif = (ccode >> 8) & 0xff; + class = ccode >> 16; + if (d->id || class == PCI_CLASS_STORAGE_IDE) { + if (d->id) + printk("%s: IDE device on PCI bus %d function %d\n", d->name, bus, fn); + else + printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n", + d->name, bus, fn, devid & 0xffff, devid >> 16); + /* + * See if IDE ports are enabled + */ + if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) { + printk("%s: error accessing PCICMD\n", d->name); + } else if ((pcicmd & 1) == 0) { + printk("%s: device disabled (BIOS)\n", d->name); + } else { + unsigned int bmiba = 0; + /* + * Check for Bus-Master DMA capability + */ + if (d->id == DEVID_PDC20246 || (class == PCI_CLASS_STORAGE_IDE && (progif & 0x80))) { + if ((!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name)))) { + printk("%s: Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, progif=0x%02x, bmiba=0x%04x\n", d->name, pcicmd, progif, bmiba); + } + } + /* + * Setup the IDE ports + */ + ide_setup_pci_device(bus, fn, bmiba, d); + } + } + } while (hedt == 0x80 && (++fn & 7)); +} + +/* + * ide_scan_pcibus() gets invoked at boot time from ide.c + */ +__initfunc(void ide_scan_pcibus (void)) +{ + unsigned int bus, dev; + + if (!pcibios_present()) + return; + for (bus = 0; bus <= 255; ++bus) { + for (dev = 0; dev <= 31; ++dev) { + ide_scan_pci_device(bus, dev << 3); + } + } +} + diff --git a/drivers/block/pdc4030.c b/drivers/block/pdc4030.c new file mode 100644 index 000000000..c161e58b6 --- /dev/null +++ b/drivers/block/pdc4030.c @@ -0,0 +1,365 @@ +/* -*- linux-c -*- + * linux/drivers/block/pdc4030.c Version 0.08 Nov 30, 1997 + * + * Copyright (C) 1995-1998 Linus Torvalds & authors (see below) + */ + +/* + * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk + * + * This file provides support for the second port and cache of Promise + * IDE interfaces, e.g. DC4030, DC5030. + * + * Thanks are due to Mark Lord for advice and patiently answering stupid + * questions, and all those mugs^H^H^H^Hbrave souls who've tested this. + * + * Version 0.01 Initial version, #include'd in ide.c rather than + * compiled separately. + * Reads use Promise commands, writes as before. Drives + * on second channel are read-only. + * Version 0.02 Writes working on second channel, reads on both + * channels. Writes fail under high load. Suspect + * transfers of >127 sectors don't work. + * Version 0.03 Brought into line with ide.c version 5.27. + * Other minor changes. + * Version 0.04 Updated for ide.c version 5.30 + * Changed initialization strategy + * Version 0.05 Kernel integration. -ml + * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml + * Version 0.07 Added support for DC4030 variants + * Secondary interface autodetection + * Version 0.08 Renamed to pdc4030.c + */ + +/* + * Once you've compiled it in, you'll have to also enable the interface + * setup routine from the kernel command line, as in + * + * 'linux ide0=dc4030' + * + * As before, it seems that somewhere around 3Megs when writing, bad things + * start to happen [timeouts/retries -ml]. If anyone can give me more feedback, + * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk] + * + */ + + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <asm/io.h> +#include <asm/irq.h> +#include "ide.h" +#include "pdc4030.h" + +/* This is needed as the controller may not interrupt if the required data is +available in the cache. We have to simulate an interrupt. Ugh! */ + +extern void ide_intr(int, void *dev_id, struct pt_regs*); + +/* + * promise_selectproc() is invoked by ide.c + * in preparation for access to the specified drive. + */ +static void promise_selectproc (ide_drive_t *drive) +{ + unsigned int number; + + OUT_BYTE(drive->select.all,IDE_SELECT_REG); + udelay(1); /* paranoia */ + number = ((HWIF(drive)->is_pdc4030_2)<<1) + drive->select.b.unit; + OUT_BYTE(number,IDE_FEATURE_REG); +} + +/* + * pdc4030_cmd handles the set of vendor specific commands that are initiated + * by command F0. They all have the same success/failure notification. + */ +int pdc4030_cmd(ide_drive_t *drive, byte cmd) +{ + unsigned long timeout, timer; + byte status_val; + + promise_selectproc(drive); /* redundant? */ + OUT_BYTE(0xF3,IDE_SECTOR_REG); + OUT_BYTE(cmd,IDE_SELECT_REG); + OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG); + timeout = HZ * 10; + timeout += jiffies; + do { + if(jiffies > timeout) { + return 2; /* device timed out */ + } + /* This is out of delay_10ms() */ + /* Delays at least 10ms to give interface a chance */ + timer = jiffies + (HZ + 99)/100 + 1; + while (timer > jiffies); + status_val = IN_BYTE(IDE_SECTOR_REG); + } while (status_val != 0x50 && status_val != 0x70); + + if(status_val == 0x50) + return 0; /* device returned success */ + else + return 1; /* device returned failure */ +} + +ide_hwif_t *hwif_required = NULL; + +void setup_pdc4030 (ide_hwif_t *hwif) +{ + hwif_required = hwif; +} + +/* +init_pdc4030: Test for presence of a Promise caching controller card. +Returns: 0 if no Promise card present at this io_base + 1 if Promise card found +*/ +int init_pdc4030 (void) +{ + ide_hwif_t *hwif = hwif_required; + ide_drive_t *drive; + ide_hwif_t *second_hwif; + struct dc_ident ident; + int i; + + if (!hwif) return 0; + + drive = &hwif->drives[0]; + second_hwif = &ide_hwifs[hwif->index+1]; + if(hwif->is_pdc4030_2) /* we've already been found ! */ + return 1; + + if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) + { + return 0; + } + OUT_BYTE(0x08,IDE_CONTROL_REG); + if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { + return 0; + } + if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + printk("%s: Failed Promise read config!\n",hwif->name); + return 0; + } + ide_input_data(drive,&ident,SECTOR_WORDS); + if(ident.id[1] != 'P' || ident.id[0] != 'T') { + return 0; + } + printk("%s: Promise caching controller, ",hwif->name); + switch(ident.type) { + case 0x43: printk("DC4030VL-2, "); break; + case 0x41: printk("DC4030VL-1, "); break; + case 0x40: printk("DC4030VL, "); break; + default: printk("unknown - type 0x%02x - please report!\n" + ,ident.type); + return 0; + } + printk("%dKB cache, ",(int)ident.cache_mem); + switch(ident.irq) { + case 0x00: hwif->irq = 14; break; + case 0x01: hwif->irq = 12; break; + default: hwif->irq = 15; break; + } + printk("on IRQ %d\n",hwif->irq); + hwif->chipset = second_hwif->chipset = ide_pdc4030; + hwif->mate = second_hwif; + second_hwif->mate = hwif; + hwif->selectproc = second_hwif->selectproc = &promise_selectproc; +/* Shift the remaining interfaces down by one */ + for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { + ide_hwif_t *h = &ide_hwifs[i]; + + printk("Shifting i/f %d values to i/f %d\n",i-1,i); + ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL); + h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET]; + h->noprobe = (h-1)->noprobe; + } + second_hwif->is_pdc4030_2 = 1; + ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL); + second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET]; + second_hwif->irq = hwif->irq; + for (i=0; i<2 ; i++) { + hwif->drives[i].io_32bit = 3; + second_hwif->drives[i].io_32bit = 3; + if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1; + } + return 1; +} + +/* + * promise_read_intr() is the handler for disk read/multread interrupts + */ +static void promise_read_intr (ide_drive_t *drive) +{ + byte stat; + int i; + unsigned int sectors_left, sectors_avail, nsect; + struct request *rq; + + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { + ide_error(drive, "promise_read_intr", stat); + return; + } + +read_again: + do { + sectors_left = IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_SECTOR_REG); + } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left); + rq = HWGROUP(drive)->rq; + sectors_avail = rq->nr_sectors - sectors_left; + +read_next: + rq = HWGROUP(drive)->rq; + if ((nsect = rq->current_nr_sectors) > sectors_avail) + nsect = sectors_avail; + sectors_avail -= nsect; + ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); +#ifdef DEBUG + printk("%s: promise_read: sectors(%ld-%ld), buffer=0x%08lx, " + "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, + (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); +#endif + rq->sector += nsect; + rq->buffer += nsect<<9; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + if ((rq->current_nr_sectors -= nsect) <= 0) + ide_end_request(1, HWGROUP(drive)); + if (i > 0) { + if (sectors_avail) + goto read_next; + stat = GET_STAT(); + if(stat & DRQ_STAT) + goto read_again; + if(stat & BUSY_STAT) { + ide_set_handler (drive, &promise_read_intr, WAIT_CMD); + return; + } + printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); + ide_error(drive, "promise read intr", stat); + } +} + +/* + * promise_write_pollfunc() is the handler for disk write completion polling. + */ +static void promise_write_pollfunc (ide_drive_t *drive) +{ + int i; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq; + + if (IN_BYTE(IDE_NSECTOR_REG) != 0) { + if (jiffies < hwgroup->poll_timeout) { + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; /* continue polling... */ + } + printk("%s: write timed-out!\n",drive->name); + ide_error (drive, "write timeout", GET_STAT()); + return; + } + + ide_multwrite(drive, 4); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + return; +} + +/* + * promise_write() transfers a block of one or more sectors of data to a + * drive as part of a disk write operation. All but 4 sectors are transfered + * in the first attempt, then the interface is polled (nicely!) for completion + * before the final 4 sectors are transfered. Don't ask me why, but this is + * how it's done in the drivers for other O/Ses. There is no interrupt + * generated on writes, which is why we have to do it like this. + */ +static void promise_write (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + int i; + + if (rq->nr_sectors > 4) { + ide_multwrite(drive, rq->nr_sectors - 4); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; + } else { + ide_multwrite(drive, rq->nr_sectors); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + } +} + +/* + * do_pdc4030_io() is called from do_rw_disk, having had the block number + * already set up. It issues a READ or WRITE command to the Promise + * controller, assuming LBA has been used to set up the block number. + */ +void do_pdc4030_io (ide_drive_t *drive, struct request *rq) +{ + unsigned long timeout; + byte stat; + + if (rq->cmd == READ) { + ide_set_handler(drive, &promise_read_intr, WAIT_CMD); + OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); +/* The card's behaviour is odd at this point. If the data is + available, DRQ will be true, and no interrupt will be + generated by the card. If this is the case, we need to simulate + an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0 + of the SELECT register will be high, so we can just return and + be interrupted.*/ + timeout = jiffies + HZ/20; /* 50ms wait */ + do { + stat=GET_STAT(); + if(stat & DRQ_STAT) { +/* unsigned long flags; + save_flags(flags); + cli(); + disable_irq(HWIF(drive)->irq); +*/ + ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); +/* enable_irq(HWIF(drive)->irq); + restore_flags(flags); +*/ + return; + } + if(IN_BYTE(IDE_SELECT_REG) & 0x01) + return; + udelay(1); + } while (jiffies < timeout); + printk("%s: reading: No DRQ and not waiting - Odd!\n", + drive->name); + return; + } + if (rq->cmd == WRITE) { + OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); + if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); + return; + } + if (!drive->unmask) + cli(); + HWGROUP(drive)->wrq = *rq; /* scratchpad */ + promise_write(drive); + return; + } + printk("%s: bad command: %d\n", drive->name, rq->cmd); + ide_end_request(0, HWGROUP(drive)); +} diff --git a/drivers/block/pdc4030.h b/drivers/block/pdc4030.h new file mode 100644 index 000000000..9f08da5aa --- /dev/null +++ b/drivers/block/pdc4030.h @@ -0,0 +1,44 @@ +/* + * linux/drivers/block/pdc4030.h + * + * Copyright (C) 1995-1998 Linus Torvalds & authors + */ + +/* + * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk> + */ + +#ifndef IDE_PROMISE_H +#define IDE_PROMISE_H + +#define PROMISE_EXTENDED_COMMAND 0xF0 +#define PROMISE_READ 0xF2 +#define PROMISE_WRITE 0xF3 +/* Extended commands - main command code = 0xf0 */ +#define PROMISE_GET_CONFIG 0x10 +#define PROMISE_IDENTIFY 0x20 + +struct translation_mode { + u16 cyl; + u8 head; + u8 sect; +}; + +struct dc_ident { + u8 type; + u8 unknown1; + u8 hw_revision; + u8 firmware_major; + u8 firmware_minor; + u8 bios_address; + u8 irq; + u8 unknown2; + u16 cache_mem; + u16 unknown3; + u8 id[2]; + u16 info; + struct translation_mode current_tm[4]; + u8 pad[SECTOR_WORDS*4 - 32]; +}; + +#endif IDE_PROMISE_H diff --git a/drivers/block/promise.c b/drivers/block/promise.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/promise.c +++ /dev/null diff --git a/drivers/block/promise.h b/drivers/block/promise.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/promise.h +++ /dev/null diff --git a/drivers/block/triton.c b/drivers/block/triton.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/block/triton.c +++ /dev/null diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c new file mode 100644 index 000000000..7b8fb308c --- /dev/null +++ b/drivers/block/trm290.c @@ -0,0 +1,227 @@ +/* + * linux/drivers/block/trm290.c Version 1.00 December 3, 1997 + * + * Copyright (c) 1997-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA function + * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards, + * including a "Precision Instruments" board. The TRM290 pre-dates + * the sff-8038 standard (ide-dma.c) by a few months, and differs + * significantly enough to warrant separate routines for some functions, + * while re-using others from ide-dma.c. + * + * EXPERIMENTAL! It works for me (a sample of one). + * + * Works reliably for me in DMA mode (READs only), + * DMA WRITEs are disabled by default (see #define below); + * + * DMA is not enabled automatically for this chipset, + * but can be turned on manually (with "hdparm -d1") at run time. + * + * I need volunteers with "spare" drives for further testing + * and development, and maybe to help figure out the peculiarities. + * Even knowing the registers (below), some things behave strangely. + */ + +#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */ + +/* + * TRM-290 PCI-IDE2 Bus Master Chip + * ================================ + * The configuration registers are addressed in normal I/O port space + * and are used as follows: + * + * 0x3df2 when WRITTEN: chiptest register (byte, write-only) + * bit7 must always be written as "1" + * bits6-2 undefined + * bit1 1=legacy_compatible_mode, 0=native_pci_mode + * bit0 1=test_mode, 0=normal(default) + * + * 0x3df2 when READ: status register (byte, read-only) + * bits7-2 undefined + * bit1 channel0 busmaster interrupt status 0=none, 1=asserted + * bit0 channel0 interrupt status 0=none, 1=asserted + * + * 0x3df3 Interrupt mask register + * bits7-5 undefined + * bit4 legacy_header: 1=present, 0=absent + * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only) + * bit2 channel1 interrupt status 0=none, 1=asserted (read only) + * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default) + * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default) + * + * 0x3df1 "CPR" Config Pointer Register (byte) + * bit7 1=autoincrement CPR bits 2-0 after each access of CDR + * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state + * bit5 0=enabled master burst access (default), 1=disable (write only) + * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast + * bit3 0=primary IDE channel, 1=secondary IDE channel + * bits2-0 register index for accesses through CDR port + * + * 0x3df0 "CDR" Config Data Register (word) + * two sets of seven config registers, + * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6), + * each index defined below: + * + * Index-0 Base address register for command block (word) + * defaults: 0x1f0 for primary, 0x170 for secondary + * + * Index-1 general config register (byte) + * bit7 1=DMA enable, 0=DMA disable + * bit6 1=activate IDE_RESET, 0=no action (default) + * bit5 1=enable IORDY, 0=disable IORDY (default) + * bit4 0=16-bit data port(default), 1=8-bit (XT) data port + * bit3 interrupt polarity: 1=active_low, 0=active_high(default) + * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only) + * bit1 bus_master_mode(?): 1=enable, 0=disable(default) + * bit0 enable_io_ports: 1=enable(default), 0=disable + * + * Index-2 read-ahead counter preload bits 0-7 (byte, write only) + * bits7-0 bits7-0 of readahead count + * + * Index-3 read-ahead config register (byte, write only) + * bit7 1=enable_readahead, 0=disable_readahead(default) + * bit6 1=clear_FIFO, 0=no_action + * bit5 undefined + * bit4 mode4 timing control: 1=enable, 0=disable(default) + * bit3 undefined + * bit2 undefined + * bits1-0 bits9-8 of read-ahead count + * + * Index-4 base address register for control block (word) + * defaults: 0x3f6 for primary, 0x376 for secondary + * + * Index-5 data port timings (shared by both drives) (byte) + * standard PCI "clk" (clock) counts, default value = 0xf5 + * + * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk + * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk, + * 011=4clk, 100=5clk, 101=6clk, + * 110=8clk, 111=12clk + * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk, + * 011=5clk, 100=6clk, 101=8clk, + * 110=12clk, 111=16clk + * + * Index-6 command/control port timings (shared by both drives) (byte) + * same layout as Index-5, default value = 0xde + * + * Suggested CDR programming for PIO mode0 (600ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary + * + * Suggested CDR programming for PIO mode3 (180ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary + * + * Suggested CDR programming for PIO mode4 (120ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/init.h> +#include <linux/hdreg.h> +#include <asm/io.h> + +#include "ide.h" + +static void select_dma_or_pio(ide_hwif_t *hwif, int dma) +{ + static int previous[2] = {-1,-1}; + unsigned long flags; + + if (previous[hwif->pci_port] != dma) { + unsigned short cfg1 = dma ? 0xa3 : 0x21; + previous[hwif->pci_port] = dma; + save_flags(flags); + cli(); + outb(0x51|(hwif->pci_port<<3),0x3df1); + outw(cfg1,0x3df0); + restore_flags(flags); + } +} + +/* + * trm290_dma_intr() is the handler for trm290 disk read/write DMA interrupts + */ +static void trm290_dma_intr (ide_drive_t *drive) +{ + byte stat; + int i; + struct request *rq = HWGROUP(drive)->rq; + + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + unsigned short dma_stat = inw(HWIF(drive)->dma_base + 2); + if (dma_stat == 0x00ff) { + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return; + } + printk("%s: bad trm290 DMA status: 0x%04x\n", drive->name, dma_stat); + } + sti(); + ide_error(drive, "dma_intr", stat); +} + +static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int count, reading = 1 << 1; + + if (drive->media == ide_disk) { + switch (func) { + case ide_dma_write: + reading = 0; +#ifdef TRM290_NO_DMA_WRITES + break; /* always use PIO for writes */ +#endif + case ide_dma_read: + if (!(count = ide_build_dmatable(drive))) + break; /* try PIO instead of DMA */ + select_dma_or_pio(hwif, 1); /* select DMA mode */ + outl_p(virt_to_bus(hwif->dmatable)|reading, hwif->dma_base); + outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */ + ide_set_handler(drive, &trm290_dma_intr, WAIT_CMD); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + return 0; + default: + return ide_dmaproc(func, drive); + } + } + select_dma_or_pio(hwif, 0); /* force PIO mode for this operation */ + return 1; +} + +static void trm290_selectproc (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + select_dma_or_pio(hwif, drive->using_dma); + OUT_BYTE(drive->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); +} + +/* + * Invoked from ide-dma.c at boot time. + */ +__initfunc(void ide_init_trm290 (byte bus, byte fn, ide_hwif_t *hwif)) +{ + hwif->chipset = ide_trm290; + hwif->selectproc = trm290_selectproc; + hwif->drives[0].autotune = 2; /* play it safe */ + hwif->drives[1].autotune = 2; /* play it safe */ + ide_setup_dma(hwif, hwif->pci_port ? 0x3d74 : 0x3df4, 2); + hwif->dmaproc = &trm290_dmaproc; +} diff --git a/drivers/cdrom/aztcd.h b/drivers/cdrom/aztcd.h new file mode 100644 index 000000000..057501e31 --- /dev/null +++ b/drivers/cdrom/aztcd.h @@ -0,0 +1,162 @@ +/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $ + * + * Definitions for a AztechCD268 CD-ROM interface + * Copyright (C) 1994-98 Werner Zimmermann + * + * based on Mitsumi CDROM driver by Martin Harriss + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: W.Zimmermann adaption to Aztech CD268-01A Version 1.3 + * October 1994 Email: Werner.Zimmermann@fht-esslingen.de + */ + +/* *** change this to set the I/O port address of your CD-ROM drive, + set to '-1', if you want autoprobing */ +#define AZT_BASE_ADDR -1 + +/* list of autoprobing addresses (not more than 15), last value must be 0x000 + Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */ +#define AZT_BASE_AUTO { 0x320, 0x300, 0x310, 0x330, 0x000 } + +/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard + and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */ +/*#define AZT_SW32 1 +*/ + +#ifdef AZT_SW32 +#define AZT_SW32_BASE_ADDR 0x220 /*I/O port base address of your soundcard*/ +#endif + +/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray + from locking */ +#define AZT_ALLOW_TRAY_LOCK 1 + +/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you + don't want the auto-eject feature*/ +#define AZT_AUTO_EJECT 0 + +/*Set this to 1, if you want to use incompatible ioctls for reading in raw and + cooked mode */ +#define AZT_PRIVATE_IOCTLS 1 + +/*Set this to 1, if you want multisession support by the ISO fs. Even if you set + this value to '0' you can use multisession CDs. In that case the drive's firm- + ware will do the appropriate redirection automatically. The CD will then look + like a single session CD (but nevertheless all data may be read). Please read + chapter '5.1 Multisession support' in README.aztcd for details. Normally it's + uncritical to leave this setting untouched */ +#define AZT_MULTISESSION 1 + +/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */ +/*#define AZT_KERNEL_PRIOR_2_1 */ + +/*---------------------------------------------------------------------------*/ +/*-----nothing to be configured for normal applications below this line------*/ + + +/* Increase this if you get lots of timeouts; if you get kernel panic, replace + STEN_LOW_WAIT by STEN_LOW in the source code */ +#define AZT_STATUS_DELAY 400 /*for timer wait, STEN_LOW_WAIT*/ +#define AZT_TIMEOUT 8000000 /*for busy wait STEN_LOW, DTEN_LOW*/ +#define AZT_FAST_TIMEOUT 10000 /*for reading the version string*/ + +/* number of times to retry a command before giving up */ +#define AZT_RETRY_ATTEMPTS 3 + +/* port access macros */ +#define CMD_PORT azt_port +#define DATA_PORT azt_port +#define STATUS_PORT azt_port+1 +#define MODE_PORT azt_port+2 +#ifdef AZT_SW32 + #define AZT_SW32_INIT (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16)) + #define AZT_SW32_CONFIG_REG AZT_SW32_BASE_ADDR+0x16 /*Soundwave32 Config. Register*/ + #define AZT_SW32_ID_REG AZT_SW32_BASE_ADDR+0x04 /*Soundwave32 ID Version Register*/ +#endif + +/* status bits */ +#define AST_CMD_CHECK 0x80 /* 1 = command error */ +#define AST_DOOR_OPEN 0x40 /* 1 = door is open */ +#define AST_NOT_READY 0x20 /* 1 = no disk in the drive */ +#define AST_DSK_CHG 0x02 /* 1 = disk removed or changed */ +#define AST_MODE 0x01 /* 0=MODE1, 1=MODE2 */ +#define AST_MODE_BITS 0x1C /* Mode Bits */ +#define AST_INITIAL 0x0C /* initial, only valid ... */ +#define AST_BUSY 0x04 /* now playing, only valid + in combination with mode + bits */ +/* flag bits */ +#define AFL_DATA 0x02 /* data available if low */ +#define AFL_STATUS 0x04 /* status available if low */ +#define AFL_OP_OK 0x01 /* OP_OK command correct*/ +#define AFL_PA_OK 0x02 /* PA_OK parameter correct*/ +#define AFL_OP_ERR 0x05 /* error in command*/ +#define AFL_PA_ERR 0x06 /* error in parameters*/ +#define POLLED 0x04 /* polled mode */ + +/* commands */ +#define ACMD_SOFT_RESET 0x10 /* reset drive */ +#define ACMD_PLAY_READ 0x20 /* read data track in cooked mode */ +#define ACMD_PLAY_READ_RAW 0x21 /* reading in raw mode*/ +#define ACMD_SEEK 0x30 /* seek msf address*/ +#define ACMD_SEEK_TO_LEADIN 0x31 /* seek to leadin track*/ +#define ACMD_GET_ERROR 0x40 /* get error code */ +#define ACMD_GET_STATUS 0x41 /* get status */ +#define ACMD_GET_Q_CHANNEL 0x50 /* read info from q channel */ +#define ACMD_EJECT 0x60 /* eject/open tray */ +#define ACMD_CLOSE 0x61 /* close tray */ +#define ACMD_LOCK 0x71 /* lock tray closed */ +#define ACMD_UNLOCK 0x72 /* unlock tray */ +#define ACMD_PAUSE 0x80 /* pause */ +#define ACMD_STOP 0x81 /* stop play */ +#define ACMD_PLAY_AUDIO 0x90 /* play audio track */ +#define ACMD_SET_VOLUME 0x93 /* set audio level */ +#define ACMD_GET_VERSION 0xA0 /* get firmware version */ +#define ACMD_SET_DISK_TYPE 0xA1 /* set disk data mode */ + +#define MAX_TRACKS 104 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct azt_Play_msf { + struct msf start; + struct msf end; +}; + +struct azt_DiskInfo { + unsigned char first; + unsigned char next; + unsigned char last; + struct msf diskLength; + struct msf firstTrack; + unsigned char multi; + struct msf nextSession; + struct msf lastSession; + unsigned char xa; + unsigned char audio; +}; + +struct azt_Toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char pointIndex; + struct msf trackTime; + struct msf diskTime; +}; diff --git a/drivers/cdrom/cdi.c b/drivers/cdrom/cdi.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/cdrom/cdi.c +++ /dev/null diff --git a/drivers/cdrom/cdu31a.h b/drivers/cdrom/cdu31a.h new file mode 100644 index 000000000..a04d0b3a2 --- /dev/null +++ b/drivers/cdrom/cdu31a.h @@ -0,0 +1,411 @@ +/* + * Definitions for a Sony interface CDROM drive. + * + * Corey Minyard (minyard@wf-rch.cirr.com) + * + * Copyright (C) 1993 Corey Minyard + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * General defines. + */ +#define SONY_XA_DISK_TYPE 0x20 + +/* + * Offsets (from the base address) and bits for the various write registers + * of the drive. + */ +#define SONY_CMD_REG_OFFSET 0 +#define SONY_PARAM_REG_OFFSET 1 +#define SONY_WRITE_REG_OFFSET 2 +#define SONY_CONTROL_REG_OFFSET 3 +# define SONY_ATTN_CLR_BIT 0x01 +# define SONY_RES_RDY_CLR_BIT 0x02 +# define SONY_DATA_RDY_CLR_BIT 0x04 +# define SONY_ATTN_INT_EN_BIT 0x08 +# define SONY_RES_RDY_INT_EN_BIT 0x10 +# define SONY_DATA_RDY_INT_EN_BIT 0x20 +# define SONY_PARAM_CLR_BIT 0x40 +# define SONY_DRIVE_RESET_BIT 0x80 + +/* + * Offsets (from the base address) and bits for the various read registers + * of the drive. + */ +#define SONY_STATUS_REG_OFFSET 0 +# define SONY_ATTN_BIT 0x01 +# define SONY_RES_RDY_BIT 0x02 +# define SONY_DATA_RDY_BIT 0x04 +# define SONY_ATTN_INT_ST_BIT 0x08 +# define SONY_RES_RDY_INT_ST_BIT 0x10 +# define SONY_DATA_RDY_INT_ST_BIT 0x20 +# define SONY_DATA_REQUEST_BIT 0x40 +# define SONY_BUSY_BIT 0x80 +#define SONY_RESULT_REG_OFFSET 1 +#define SONY_READ_REG_OFFSET 2 +#define SONY_FIFOST_REG_OFFSET 3 +# define SONY_PARAM_WRITE_RDY_BIT 0x01 +# define SONY_PARAM_REG_EMPTY_BIT 0x02 +# define SONY_RES_REG_NOT_EMP_BIT 0x04 +# define SONY_RES_REG_FULL_BIT 0x08 + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_DETECT_TIMEOUT (8*HZ/10) /* Maximum amount of time + that drive detection code + will wait for response + from drive (in 1/100th's + of seconds). */ + +#define SONY_JIFFIES_TIMEOUT 1000 /* Maximum number of times the + drive will wait/try for an + operation */ +#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the + drive will wait/try a reset + operation */ +#define SONY_READY_RETRIES 20000 /* How many times to retry a + spin waiting for a register + to come ready */ + +#define MAX_CDU31A_RETRIES 3 /* How many times to retry an + operation */ + +/* Commands to request or set drive control parameters and disc information */ +#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */ +#define SONY_REQ_DRIVE_MODE_CMD 0x01 +#define SONY_REQ_DRIVE_PARAM_CMD 0x02 +#define SONY_REQ_MECH_STATUS_CMD 0x03 +#define SONY_REQ_AUDIO_STATUS_CMD 0x04 +#define SONY_SET_DRIVE_PARAM_CMD 0x10 +#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */ +#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */ +#define SONY_REQ_UPC_EAN_CMD 0x22 +#define SONY_REQ_ISRC_CMD 0x23 +#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24 /* Returns s_sony_session_toc */ + +/* Commands to request information from the drive */ +#define SONY_READ_TOC_CMD 0x30 /* let the drive firmware grab the TOC */ +#define SONY_SEEK_CMD 0x31 +#define SONY_READ_CMD 0x32 +#define SONY_READ_BLKERR_STAT_CMD 0x34 +#define SONY_ABORT_CMD 0x35 +#define SONY_READ_TOC_SPEC_CMD 0x36 + +/* Commands to control audio */ +#define SONY_AUDIO_PLAYBACK_CMD 0x40 +#define SONY_AUDIO_STOP_CMD 0x41 +#define SONY_AUDIO_SCAN_CMD 0x42 + +/* Miscellaneous control commands */ +#define SONY_EJECT_CMD 0x50 +#define SONY_SPIN_UP_CMD 0x51 +#define SONY_SPIN_DOWN_CMD 0x52 + +/* Diagnostic commands */ +#define SONY_WRITE_BUFFER_CMD 0x60 +#define SONY_READ_BUFFER_CMD 0x61 +#define SONY_DIAGNOSTICS_CMD 0x62 + + +/* + * The following are command parameters for the set drive parameter command + */ +#define SONY_SD_DECODE_PARAM 0x00 +#define SONY_SD_INTERFACE_PARAM 0x01 +#define SONY_SD_BUFFERING_PARAM 0x02 +#define SONY_SD_AUDIO_PARAM 0x03 +#define SONY_SD_AUDIO_VOLUME 0x04 +#define SONY_SD_MECH_CONTROL 0x05 +#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06 + +/* + * The following are parameter bits for the mechanical control command + */ +#define SONY_AUTO_SPIN_UP_BIT 0x01 +#define SONY_AUTO_EJECT_BIT 0x02 +#define SONY_DOUBLE_SPEED_BIT 0x04 + +/* + * The following extract information from the drive configuration about + * the drive itself. + */ +#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) +#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) +#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) +#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10) +#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) +#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) +#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) +#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) + +#define SONY_HWC_CADDY_LOAD_MECH 0x00 +#define SONY_HWC_TRAY_LOAD_MECH 0x01 +#define SONY_HWC_POPUP_LOAD_MECH 0x02 +#define SONY_HWC_UNKWN_LOAD_MECH 0x03 + +#define SONY_HWC_8KB_BUFFER 0x00 +#define SONY_HWC_32KB_BUFFER 0x01 +#define SONY_HWC_64KB_BUFFER 0x02 +#define SONY_HWC_UNKWN_BUFFER 0x03 + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s_sony_drive_config +{ + unsigned char exec_status[2]; + char vendor_id[8]; + char product_id[16]; + char product_rev_level[8]; + unsigned char hw_config[2]; +}; + +/* The following is returned from the request subcode address command */ +struct s_sony_subcode +{ + unsigned char exec_status[2]; + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char reserved1; + unsigned char abs_msf[3]; +}; + +#define MAX_TRACKS 100 /* The maximum tracks a disk may have. */ +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s_sony_toc +{ + unsigned char exec_status[2]; + unsigned char address0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char disk_type; + unsigned char dummy0; + unsigned char address1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char address2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int lead_out_start_lba; +}; + +struct s_sony_session_toc +{ + unsigned char exec_status[2]; + unsigned char session_number; + unsigned char address0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char disk_type; + unsigned char dummy0; + unsigned char address1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char address2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + unsigned char addressb0 :4; + unsigned char controlb0 :4; + unsigned char pointb0; + unsigned char next_poss_prog_area_msf[3]; + unsigned char num_mode_5_pointers; + unsigned char max_start_outer_leadout_msf[3]; + unsigned char addressb1 :4; + unsigned char controlb1 :4; + unsigned char pointb1; + unsigned char dummyb0_1[4]; + unsigned char num_skip_interval_pointers; + unsigned char num_skip_track_assignments; + unsigned char dummyb0_2; + unsigned char addressb2 :4; + unsigned char controlb2 :4; + unsigned char pointb2; + unsigned char tracksb2[7]; + unsigned char addressb3 :4; + unsigned char controlb3 :4; + unsigned char pointb3; + unsigned char tracksb3[7]; + unsigned char addressb4 :4; + unsigned char controlb4 :4; + unsigned char pointb4; + unsigned char tracksb4[7]; + unsigned char addressc0 :4; + unsigned char controlc0 :4; + unsigned char pointc0; + unsigned char dummyc0[7]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int start_track_lba; + unsigned int lead_out_start_lba; + unsigned int mint; + unsigned int maxt; +}; + +struct s_all_sessions_toc +{ + unsigned char sessions; + unsigned int track_entries; + unsigned char first_track_num; + unsigned char last_track_num; + unsigned char disk_type; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int start_track_lba; + unsigned int lead_out_start_lba; +}; + + +/* + * The following are errors returned from the drive. + */ + +/* Command error group */ +#define SONY_ILL_CMD_ERR 0x10 +#define SONY_ILL_PARAM_ERR 0x11 + +/* Mechanism group */ +#define SONY_NOT_LOAD_ERR 0x20 +#define SONY_NO_DISK_ERR 0x21 +#define SONY_NOT_SPIN_ERR 0x22 +#define SONY_SPIN_ERR 0x23 +#define SONY_SPINDLE_SERVO_ERR 0x25 +#define SONY_FOCUS_SERVO_ERR 0x26 +#define SONY_EJECT_MECH_ERR 0x29 +#define SONY_AUDIO_PLAYING_ERR 0x2a +#define SONY_EMERGENCY_EJECT_ERR 0x2c + +/* Seek error group */ +#define SONY_FOCUS_ERR 0x30 +#define SONY_FRAME_SYNC_ERR 0x31 +#define SONY_SUBCODE_ADDR_ERR 0x32 +#define SONY_BLOCK_SYNC_ERR 0x33 +#define SONY_HEADER_ADDR_ERR 0x34 + +/* Read error group */ +#define SONY_ILL_TRACK_R_ERR 0x40 +#define SONY_MODE_0_R_ERR 0x41 +#define SONY_ILL_MODE_R_ERR 0x42 +#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 +#define SONY_MODE_R_ERR 0x44 +#define SONY_FORM_R_ERR 0x45 +#define SONY_LEAD_OUT_R_ERR 0x46 +#define SONY_BUFFER_OVERRUN_R_ERR 0x47 + +/* Data error group */ +#define SONY_UNREC_CIRC_ERR 0x53 +#define SONY_UNREC_LECC_ERR 0x57 + +/* Subcode error group */ +#define SONY_NO_TOC_ERR 0x60 +#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 +#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 +#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 +#define SONY_TOC_DATA_ERR 0x65 + +/* Hardware failure group */ +#define SONY_HW_FAILURE_ERR 0x70 +#define SONY_LEAD_IN_A_ERR 0x91 +#define SONY_LEAD_OUT_A_ERR 0x92 +#define SONY_DATA_TRACK_A_ERR 0x93 + +/* + * The following are returned from the Read With Block Error Status command. + * They are not errors but information (Errors from the 0x5x group above may + * also be returned + */ +#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 +#define SONY_NO_LECC_ERR_BLK_STAT 0x54 +#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 +#define SONY_NO_ERR_DETECTION_STAT 0x59 + +/* + * The following is not an error returned by the drive, but by the code + * that talks to the drive. It is returned because of a timeout. + */ +#define SONY_TIMEOUT_OP_ERR 0x01 +#define SONY_SIGNAL_OP_ERR 0x02 +#define SONY_BAD_DATA_ERR 0x03 + + +/* + * The following are attention code for asynchronous events from the drive. + */ + +/* Standard attention group */ +#define SONY_EMER_EJECT_ATTN 0x2c +#define SONY_HW_FAILURE_ATTN 0x70 +#define SONY_MECH_LOADED_ATTN 0x80 +#define SONY_EJECT_PUSHED_ATTN 0x81 + +/* Audio attention group */ +#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 +#define SONY_LEAD_IN_ERR_ATTN 0x91 +#define SONY_LEAD_OUT_ERR_ATTN 0x92 +#define SONY_DATA_TRACK_ERR_ATTN 0x93 +#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 + +/* Auto spin up group */ +#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 +#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 +#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 +#define SONY_TOC_READ_DONE_ATTN 0x62 +#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 +#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 + +/* Auto eject group */ +#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 +#define SONY_EJECT_COMPLETE_ATTN 0x28 +#define SONY_EJECT_MECH_ERR_ATTN 0x29 diff --git a/drivers/cdrom/cm206.h b/drivers/cdrom/cm206.h new file mode 100644 index 000000000..e5ba8ff20 --- /dev/null +++ b/drivers/cdrom/cm206.h @@ -0,0 +1,171 @@ +/* cm206.h Header file for cm206.c. + Copyright (c) 1995 David van Leeuwen +*/ + +#ifndef LINUX_CM206_H +#define LINUX_CM206_H + +#include <linux/ioctl.h> + +/* First, the cm260 stuff */ +/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined + below, the values are not used unless autoprobing is turned off and + no LILO boot options or module command line options are given. Change + these values to your own as last resort if autoprobing and options + don't work. */ + +#define CM206_BASE 0x340 +#define CM206_IRQ 11 + +#define r_data_status (cm206_base) +#define r_uart_receive (cm206_base+0x2) +#define r_fifo_output_buffer (cm206_base+0x4) +#define r_line_status (cm206_base+0x6) +#define r_data_control (cm206_base+0x8) +#define r_uart_transmit (cm206_base+0xa) +#define r_test_clock (cm206_base+0xc) +#define r_test_control (cm206_base+0xe) + +/* the data_status flags */ +#define ds_ram_size 0x4000 +#define ds_toc_ready 0x2000 +#define ds_fifo_empty 0x1000 +#define ds_sync_error 0x800 +#define ds_crc_error 0x400 +#define ds_data_error 0x200 +#define ds_fifo_overflow 0x100 +#define ds_data_ready 0x80 + +/* the line_status flags */ +#define ls_attention 0x10 +#define ls_parity_error 0x8 +#define ls_overrun 0x4 +#define ls_receive_buffer_full 0x2 +#define ls_transmitter_buffer_empty 0x1 + +/* the data control register flags */ +#define dc_read_q_channel 0x4000 +#define dc_mask_sync_error 0x2000 +#define dc_toc_enable 0x1000 +#define dc_no_stop_on_error 0x800 +#define dc_break 0x400 +#define dc_initialize 0x200 +#define dc_mask_transmit_ready 0x100 +#define dc_flag_enable 0x80 + +/* Define the default data control register flags here */ +#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \ + dc_mask_transmit_ready) + +/* now some constants related to the cm206 */ +/* another drive status byte, echoed by the cm206 on most commands */ + +#define dsb_error_condition 0x1 +#define dsb_play_in_progress 0x4 +#define dsb_possible_media_change 0x8 +#define dsb_disc_present 0x10 +#define dsb_drive_not_ready 0x20 +#define dsb_tray_locked 0x40 +#define dsb_tray_not_closed 0x80 + +#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed) + +/* the cm206 command set */ + +#define c_close_tray 0 +#define c_lock_tray 0x01 +#define c_unlock_tray 0x04 +#define c_open_tray 0x05 +#define c_seek 0x10 +#define c_read_data 0x20 +#define c_force_1x 0x21 +#define c_force_2x 0x22 +#define c_auto_mode 0x23 +#define c_play 0x30 +#define c_set_audio_mode 0x31 +#define c_read_current_q 0x41 +#define c_stream_q 0x42 +#define c_drive_status 0x50 +#define c_disc_status 0x51 +#define c_audio_status 0x52 +#define c_drive_configuration 0x53 +#define c_read_upc 0x60 +#define c_stop 0x70 +#define c_calc_checksum 0xe5 + +#define c_gimme 0xf8 + +/* finally, the (error) condition that the drive can be in * + * OK, this is not always an error, but let's prefix it with e_ */ + +#define e_none 0 +#define e_illegal_command 0x01 +#define e_sync 0x02 +#define e_seek 0x03 +#define e_parity 0x04 +#define e_focus 0x05 +#define e_header_sync 0x06 +#define e_code_incompatibility 0x07 +#define e_reset_done 0x08 +#define e_bad_parameter 0x09 +#define e_radial 0x0a +#define e_sub_code 0x0b +#define e_no_data_track 0x0c +#define e_scan 0x0d +#define e_tray_open 0x0f +#define e_no_disc 0x10 +#define e_tray stalled 0x11 + +/* drive configuration masks */ + +#define dcf_revision_code 0x7 +#define dcf_transfer_rate 0x60 +#define dcf_motorized_tray 0x80 + +/* disc status byte */ + +#define cds_multi_session 0x2 +#define cds_all_audio 0x8 +#define cds_xa_mode 0xf0 + +/* finally some ioctls for the driver */ + +#define CM206CTL_GET_STAT _IO( 0x20, 0 ) +#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 ) + +#ifdef STATISTICS + +/* This is an ugly way to guarantee that the names of the statistics + * are the same in the code and in the diagnostics program. */ + +#ifdef __KERNEL__ +#define x(a) st_ ## a +#define y enum +#else +#define x(a) #a +#define y char * stats_name[] = +#endif + +y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error), + x(crc_error), x(sync_error), x(lost_intr), x(echo), + x(write_timeout), x(receive_timeout), x(read_timeout), + x(dsb_timeout), x(stop_0xff), x(back_read_timeout), + x(sector_transferred), x(read_restarted), x(read_background), + x(bh), x(open), x(ioctl_multisession), x(attention) +#ifdef __KERNEL__ + , x(last_entry) +#endif + }; + +#ifdef __KERNEL__ +#define NR_STATS st_last_entry +#else +#define NR_STATS (sizeof(stats_name)/sizeof(char*)) +#endif + +#undef y +#undef x + +#endif STATISTICS + +#endif LINUX_CM206_H diff --git a/drivers/cdrom/gscd.h b/drivers/cdrom/gscd.h new file mode 100644 index 000000000..fccc742a5 --- /dev/null +++ b/drivers/cdrom/gscd.h @@ -0,0 +1,110 @@ +/* + * Definitions for a GoldStar R420 CD-ROM interface + * + * Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de> + * Eberhard Moenkeberg <emoenke@gwdg.de> + * + * Published under the GPL. + * + */ + + +/* The Interface Card default address is 0x340. This will work for most + applications. Address selection is accomplished by jumpers PN801-1 to + PN801-4 on the GoldStar Interface Card. + Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360 + 0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0 */ + +/* insert here the I/O port address */ +#define GSCD_BASE_ADDR 0x340 + +/* change this to set the dma-channel */ +#define GSCD_DMA_CHANNEL 3 /* not used */ + +/************** nothing to set up below here *********************/ + +/* port access macro */ +#define GSCDPORT(x) (gscd_port + (x)) + +/* + * commands + * the lower nibble holds the command length + */ +#define CMD_STATUS 0x01 +#define CMD_READSUBQ 0x02 /* 1: ?, 2: UPC, 5: ? */ +#define CMD_SEEK 0x05 /* read_mode M-S-F */ +#define CMD_READ 0x07 /* read_mode M-S-F nsec_h nsec_l */ +#define CMD_RESET 0x11 +#define CMD_SETMODE 0x15 +#define CMD_PLAY 0x17 /* M-S-F M-S-F */ +#define CMD_LOCK_CTL 0x22 /* 0: unlock, 1: lock */ +#define CMD_IDENT 0x31 +#define CMD_SETSPEED 0x32 /* 0: auto */ /* ??? */ +#define CMD_GETMODE 0x41 +#define CMD_PAUSE 0x51 +#define CMD_READTOC 0x61 +#define CMD_DISKINFO 0x71 +#define CMD_TRAY_CTL 0x81 + +/* + * disk_state: + */ +#define ST_PLAYING 0x80 +#define ST_UNLOCKED 0x40 +#define ST_NO_DISK 0x20 +#define ST_DOOR_OPEN 0x10 +#define ST_x08 0x08 +#define ST_x04 0x04 +#define ST_INVALID 0x02 +#define ST_x01 0x01 + +/* + * cmd_type: + */ +#define TYPE_INFO 0x01 +#define TYPE_DATA 0x02 + +/* + * read_mode: + */ +#define MOD_POLLED 0x80 +#define MOD_x08 0x08 +#define MOD_RAW 0x04 + +#define READ_DATA(port, buf, nr) insb(port, buf, nr) + +#define SET_TIMER(func, jifs) \ + ((timer_table[GSCD_TIMER].expires = jiffies + jifs), \ + (timer_table[GSCD_TIMER].fn = func), \ + (timer_active |= 1<<GSCD_TIMER)) + +#define CLEAR_TIMER timer_active &= ~(1<<GSCD_TIMER) + +#define MAX_TRACKS 104 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct gscd_Play_msf { + struct msf start; + struct msf end; +}; + +struct gscd_DiskInfo { + unsigned char first; + unsigned char last; + struct msf diskLength; + struct msf firstTrack; +}; + +struct gscd_Toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char pointIndex; + struct msf trackTime; + struct msf diskTime; +}; + diff --git a/drivers/cdrom/isp16.h b/drivers/cdrom/isp16.h new file mode 100644 index 000000000..9945bb34c --- /dev/null +++ b/drivers/cdrom/isp16.h @@ -0,0 +1,75 @@ +/* -- isp16.h + * + * Header for detection and initialisation of cdrom interface (only) on + * ISP16 (MAD16, Mozart) sound card. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* These are the default values */ +#define ISP16_CDROM_TYPE "Sanyo" +#define ISP16_CDROM_IO_BASE 0x340 +#define ISP16_CDROM_IRQ 0 +#define ISP16_CDROM_DMA 0 + +/* Some (Media)Magic */ +/* define types of drive the interface on an ISP16 card may be looking at */ +#define ISP16_DRIVE_X 0x00 +#define ISP16_SONY 0x02 +#define ISP16_PANASONIC0 0x02 +#define ISP16_SANYO0 0x02 +#define ISP16_MITSUMI 0x04 +#define ISP16_PANASONIC1 0x06 +#define ISP16_SANYO1 0x06 +#define ISP16_DRIVE_NOT_USED 0x08 /* not used */ +#define ISP16_DRIVE_SET_MASK 0xF1 /* don't change 0-bit or 4-7-bits*/ +/* ...for port */ +#define ISP16_DRIVE_SET_PORT 0xF8D +/* set io parameters */ +#define ISP16_BASE_340 0x00 +#define ISP16_BASE_330 0x40 +#define ISP16_BASE_360 0x80 +#define ISP16_BASE_320 0xC0 +#define ISP16_IRQ_X 0x00 +#define ISP16_IRQ_5 0x04 /* shouldn't be used to avoid sound card conflicts */ +#define ISP16_IRQ_7 0x08 /* shouldn't be used to avoid sound card conflicts */ +#define ISP16_IRQ_3 0x0C +#define ISP16_IRQ_9 0x10 +#define ISP16_IRQ_10 0x14 +#define ISP16_IRQ_11 0x18 +#define ISP16_DMA_X 0x03 +#define ISP16_DMA_3 0x00 +#define ISP16_DMA_5 0x00 +#define ISP16_DMA_6 0x01 +#define ISP16_DMA_7 0x02 +#define ISP16_IO_SET_MASK 0x20 /* don't change 5-bit */ +/* ...for port */ +#define ISP16_IO_SET_PORT 0xF8E +/* enable the card */ +#define ISP16_C928__ENABLE_PORT 0xF90 /* ISP16 with OPTi 82C928 chip */ +#define ISP16_C929__ENABLE_PORT 0xF91 /* ISP16 with OPTi 82C929 chip */ +#define ISP16_ENABLE_CDROM 0x80 /* seven bit */ + +/* the magic stuff */ +#define ISP16_CTRL_PORT 0xF8F +#define ISP16_C928__CTRL 0xE2 /* ISP16 with OPTi 82C928 chip */ +#define ISP16_C929__CTRL 0xE3 /* ISP16 with OPTi 82C929 chip */ + +#define ISP16_IO_BASE 0xF8D +#define ISP16_IO_SIZE 5 /* ports used from 0xF8D up to 0xF91 */ + +void isp16_setup(char *str, int *ints); +int isp16_init(void); diff --git a/drivers/cdrom/mcd.h b/drivers/cdrom/mcd.h new file mode 100644 index 000000000..9997f4390 --- /dev/null +++ b/drivers/cdrom/mcd.h @@ -0,0 +1,128 @@ +/* + * Definitions for a Mitsumi CD-ROM interface + * + * Copyright (C) 1992 Martin Harriss + * + * martin@bdsi.com + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* *** change this to set the I/O port address */ +#define MCD_BASE_ADDR 0x300 + +/* *** change this to set the interrupt number */ +#define MCD_INTR_NR 11 + +/* *** make the following line uncommented, if you're sure, + * *** all configuration is done */ + +/* #define I_WAS_HERE */ + + + + +/* Increase this if you get lots of timeouts */ +#define MCD_STATUS_DELAY 1000 + +/* number of times to retry a command before giving up */ +#define MCD_RETRY_ATTEMPTS 10 + +/* port access macro */ +#define MCDPORT(x) (mcd_port + (x)) + +/* How many sectors to read at 1x when an error at 2x speed occurs. */ +/* You can change this to anything from 2 to 32767, but 30 seems to */ +/* work best for me. I have found that when the drive has problems */ +/* reading one sector, it will have troubles reading the next few. */ +#define SINGLE_HOLD_SECTORS 30 + +#define MCMD_2X_READ 0xC1 /* Double Speed Read DON'T TOUCH! */ + +/* status bits */ + +#define MST_CMD_CHECK 0x01 /* command error */ +#define MST_BUSY 0x02 /* now playing */ +#define MST_READ_ERR 0x04 /* read error */ +#define MST_DSK_TYPE 0x08 +#define MST_SERVO_CHECK 0x10 +#define MST_DSK_CHG 0x20 /* disk removed or changed */ +#define MST_READY 0x40 /* disk in the drive */ +#define MST_DOOR_OPEN 0x80 /* door is open */ + +/* flag bits */ + +#define MFL_DATA 0x02 /* data available */ +#define MFL_STATUS 0x04 /* status available */ + +/* commands */ + +#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */ +#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */ +#define MCMD_GET_STATUS 0x40 +#define MCMD_SET_MODE 0x50 +#define MCMD_SOFT_RESET 0x60 +#define MCMD_STOP 0x70 /* stop play */ +#define MCMD_CONFIG_DRIVE 0x90 +#define MCMD_SET_VOLUME 0xAE /* set audio level */ +#define MCMD_PLAY_READ 0xC0 /* play or read data */ +#define MCMD_GET_VERSION 0xDC +#define MCMD_EJECT 0xF6 /* eject (FX drive) */ + +/* borrowed from hd.c */ + +#define READ_DATA(port, buf, nr) \ +insb(port, buf, nr) + +#define SET_TIMER(func, jifs) \ + ((timer_table[MCD_TIMER].expires = jiffies + jifs), \ + (timer_table[MCD_TIMER].fn = func), \ + (timer_active |= 1<<MCD_TIMER)) + +#define CLEAR_TIMER timer_active &= ~(1<<MCD_TIMER) + +#define MAX_TRACKS 104 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct mcd_Play_msf { + struct msf start; + struct msf end; +}; + +struct mcd_DiskInfo { + unsigned char first; + unsigned char last; + struct msf diskLength; + struct msf firstTrack; +}; + +struct mcd_Toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char pointIndex; + struct msf trackTime; + struct msf diskTime; +}; + +#ifndef I_WAS_HERE +#warning You have not edited mcd.h +#warning Perhaps irq and i/o settings are wrong. +#endif diff --git a/drivers/cdrom/mcdx.h b/drivers/cdrom/mcdx.h new file mode 100644 index 000000000..b1b431326 --- /dev/null +++ b/drivers/cdrom/mcdx.h @@ -0,0 +1,184 @@ +/* + * Definitions for the Mitsumi CDROM interface + * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de> + * VERSION: @VERSION@ + * + * 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, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Thanks to + * The Linux Community at all and ... + * Martin Harris (he wrote the first Mitsumi Driver) + * Eberhard Moenkeberg (he gave me much support and the initial kick) + * Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they + * improved the original driver) + * Jon Tombs, Bjorn Ekwall (module support) + * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) + * Gerd Knorr (he lent me his PhotoCD) + * Nils Faerber and Roger E. Wolff (extensively tested the LU portion) + * Andreas Kies (testing the mysterious hang up's) + * ... somebody forgotten? + * Marcin Dalecki + * + */ + +/* + * The following lines are for user configuration + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * {0|1} -- 1 if you want the driver detect your drive, may crash and + * needs a long time to seek. The higher the address the longer the + * seek. + * + * WARNING: AUTOPROBE doesn't work. + */ +#define MCDX_AUTOPROBE 0 + +/* + * Drive specific settings according to the jumpers on the controller + * board(s). + * o MCDX_NDRIVES : number of used entries of the following table + * o MCDX_DRIVEMAP : table of {i/o base, irq} per controller + * + * NOTE: I didn't get a drive at irq 9(2) working. Not even alone. + */ +#if MCDX_AUTOPROBE == 0 + #define MCDX_NDRIVES 1 + #define MCDX_DRIVEMAP { \ + {0x300, 11}, \ + {0x304, 05}, \ + {0x000, 00}, \ + {0x000, 00}, \ + {0x000, 00}, \ + } +#else + #error Autoprobing is not implemented yet. +#endif + +#ifndef MCDX_QUIET +#define MCDX_QUIET 1 +#endif + +#ifndef MCDX_DEBUG +#define MCDX_DEBUG 0 +#endif + +/* *** make the following line uncommented, if you're sure, + * *** all configuration is done */ +/* #define I_WAS_HERE */ +#define I_WAS_HERE /* delete this line, it's for heiko only */ + +/* The name of the device */ +#define MCDX "mcdx" + +/* Flags for DEBUGGING */ +#define INIT 0 +#define MALLOC 0 +#define IOCTL 0 +#define PLAYTRK 0 +#define SUBCHNL 0 +#define TOCHDR 0 +#define MS 0 +#define PLAYMSF 0 +#define READTOC 0 +#define OPENCLOSE 0 +#define HW 0 +#define TALK 0 +#define IRQ 0 +#define XFER 0 +#define REQUEST 0 +#define SLEEP 0 + +/* The following addresses are taken from the Mitsumi Reference + * and describe the possible i/o range for the controller. + */ +#define MCDX_IO_BEGIN ((char*) 0x300) /* first base of i/o addr */ +#define MCDX_IO_END ((char*) 0x3fc) /* last base of i/o addr */ + +/* Per controller 4 bytes i/o are needed. */ +#define MCDX_IO_SIZE 4 + +/* + * Bits + */ + +/* The status byte, returned from every command, set if + * the description is true */ +#define MCDX_RBIT_OPEN 0x80 /* door is open */ +#define MCDX_RBIT_DISKSET 0x40 /* disk set (recognised) */ +#define MCDX_RBIT_CHANGED 0x20 /* disk was changed */ +#define MCDX_RBIT_CHECK 0x10 /* disk rotates, servo is on */ +#define MCDX_RBIT_AUDIOTR 0x08 /* current track is audio */ +#define MCDX_RBIT_RDERR 0x04 /* read error, refer SENSE KEY */ +#define MCDX_RBIT_AUDIOBS 0x02 /* currently playing audio */ +#define MCDX_RBIT_CMDERR 0x01 /* command, param or format error */ + +/* The I/O Register holding the h/w status of the drive, + * can be read at i/o base + 1 */ +#define MCDX_RBIT_DOOR 0x10 /* door is open */ +#define MCDX_RBIT_STEN 0x04 /* if 0, i/o base contains drive status */ +#define MCDX_RBIT_DTEN 0x02 /* if 0, i/o base contains data */ + +/* + * The commands. + */ + +#define OPCODE 1 /* offset of opcode */ +#define MCDX_CMD_REQUEST_TOC 1, 0x10 +#define MCDX_CMD_REQUEST_STATUS 1, 0x40 +#define MCDX_CMD_RESET 1, 0x60 +#define MCDX_CMD_REQUEST_DRIVE_MODE 1, 0xc2 +#define MCDX_CMD_SET_INTERLEAVE 2, 0xc8, 0 +#define MCDX_CMD_DATAMODE_SET 2, 0xa0, 0 + #define MCDX_DATAMODE1 0x01 + #define MCDX_DATAMODE2 0x02 +#define MCDX_CMD_LOCK_DOOR 2, 0xfe, 0 + +#define READ_AHEAD 4 /* 8 Sectors (4K) */ + +/* Useful macros */ +#define e_door(x) ((x) & MCDX_RBIT_OPEN) +#define e_check(x) (~(x) & MCDX_RBIT_CHECK) +#define e_notset(x) (~(x) & MCDX_RBIT_DISKSET) +#define e_changed(x) ((x) & MCDX_RBIT_CHANGED) +#define e_audio(x) ((x) & MCDX_RBIT_AUDIOTR) +#define e_audiobusy(x) ((x) & MCDX_RBIT_AUDIOBS) +#define e_cmderr(x) ((x) & MCDX_RBIT_CMDERR) +#define e_readerr(x) ((x) & MCDX_RBIT_RDERR) + +/** no drive specific */ +#define MCDX_CDBLK 2048 /* 2048 cooked data each blk */ + +#define MCDX_DATA_TIMEOUT (HZ/10) /* 0.1 second */ + +/* + * Access to the msf array + */ +#define MSF_MIN 0 /* minute */ +#define MSF_SEC 1 /* second */ +#define MSF_FRM 2 /* frame */ + +/* + * Errors + */ +#define MCDX_E 1 /* unspec error */ +#define MCDX_ST_EOM 0x0100 /* end of media */ +#define MCDX_ST_DRV 0x00ff /* mask to query the drive status */ + +#ifndef I_WAS_HERE +#warning You have not edited mcdx.h +#warning Perhaps irq and i/o settings are wrong. +#endif + +/* ex:set ts=4 sw=4: */ diff --git a/drivers/cdrom/optcd.h b/drivers/cdrom/optcd.h new file mode 100644 index 000000000..00dd3aed8 --- /dev/null +++ b/drivers/cdrom/optcd.h @@ -0,0 +1,52 @@ +/* linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver + $Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $ + + Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) + + + Configuration file for linux/drivers/cdrom/optcd.c +*/ + +#ifndef _LINUX_OPTCD_H +#define _LINUX_OPTCD_H + + +/* I/O base of drive. Drive uses base to base+2. + This setting can be overridden with the kernel or insmod command + line option 'optcd=<portbase>'. Use address of 0 to disable driver. */ +#define OPTCD_PORTBASE 0x340 + + +/* enable / disable parts of driver by define / undef */ +#define MULTISESSION /* multisession support (ALPHA) */ + + +/* Change 0 to 1 to debug various parts of the driver */ +#define DEBUG_DRIVE_IF 0 /* Low level drive interface */ +#define DEBUG_CONV 0 /* Address conversions */ +#define DEBUG_BUFFERS 0 /* Buffering and block size conversion */ +#define DEBUG_REQUEST 0 /* Request mechanism */ +#define DEBUG_STATE 0 /* State machine */ +#define DEBUG_TOC 0 /* Q-channel and Table of Contents */ +#define DEBUG_MULTIS 0 /* Multisession code */ +#define DEBUG_VFS 0 /* VFS interface */ + + +/* Don't touch these unless you know what you're doing. */ + +/* Various timeout loop repetition counts. */ +#define BUSY_TIMEOUT 10000000 /* for busy wait */ +#define FAST_TIMEOUT 100000 /* ibid. for probing */ +#define SLEEP_TIMEOUT 6000 /* for timer wait */ +#define MULTI_SEEK_TIMEOUT 1000 /* for timer wait */ +#define READ_TIMEOUT 6000 /* for poll wait */ +#define STOP_TIMEOUT 2000 /* for poll wait */ +#define RESET_WAIT 5000 /* busy wait at drive reset */ + +/* # of buffers for block size conversion. 6 is optimal for my setup (P75), + giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal + setting */ +#define N_BUFS 6 + + +#endif _LINUX_OPTCD_H diff --git a/drivers/cdrom/sbpcd.h b/drivers/cdrom/sbpcd.h new file mode 100644 index 000000000..e9021316d --- /dev/null +++ b/drivers/cdrom/sbpcd.h @@ -0,0 +1,858 @@ +/* + * sbpcd.h Specify interface address and interface type here. + */ + +/* + * Attention! This file contains user-serviceable parts! + * I recommend to make use of it... + * If you feel helpless, look into linux/Documentation/cdrom/sbpcd + * (good idea anyway, at least before mailing me). + * + * The definitions for the first controller can get overridden by + * the kernel command line ("lilo boot option"). + * Examples: + * sbpcd=0x300,LaserMate + * or + * sbpcd=0x230,SoundBlaster + * or + * sbpcd=0x338,SoundScape + * or + * sbpcd=0x2C0,Teac16bit + * + * If sbpcd gets used as a module, you can load it with + * insmod sbpcd.o sbpcd=0x300,0 + * or + * insmod sbpcd.o sbpcd=0x230,1 + * or + * insmod sbpcd.o sbpcd=0x338,2 + * or + * insmod sbpcd.o sbpcd=0x2C0,3 + * respective to override the configured address and type. + */ + +/* + * define your CDROM port base address as CDROM_PORT + * and specify the type of your interface card as SBPRO. + * + * address: + * ======== + * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ... + * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ... + * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to + * specify the REAL address here, not the configuration port address. Look + * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let + * sbpcd auto-probe, if you are not firm with the address. + * There are some soundcards on the market with 0x0630, 0x0650, ...; their + * type is not obvious (both types are possible). + * + * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1. + * if your soundcard has its CDROM port above 0x300, specify + * that address and try SBPRO 0 first. + * if your SoundScape configuration port is at 0x330, specify + * 0x338 and SBPRO 2. + * + * interface type: + * =============== + * set SBPRO to 1 for "true" SoundBlaster card + * set SBPRO to 0 for "compatible" soundcards and + * for "poor" (no sound) interface cards. + * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards + * set SBPRO to 3 for Teac 16bit interface cards + * + * Almost all "compatible" sound boards need to set SBPRO to 0. + * If SBPRO is set wrong, the drives will get found - but any + * data access will give errors (audio access will work). + * The "OmniCD" no-sound interface card from CreativeLabs and most Teac + * interface cards need SBPRO 1. + * + * sound base: + * =========== + * The SOUND_BASE definition tells if we should try to turn the CD sound + * channels on. It will only be of use regarding soundcards with a SbPro + * compatible mixer. + * + * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels + * #define SOUND_BASE 0 leaves the soundcard untouched + */ +#if !(SBPCD_ISSUE-1) /* first (or if you have only one) interface board: */ +#define CDROM_PORT 0x340 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-2) /* ==================== second interface board: === */ +#define CDROM_PORT 0x344 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-3) /* ===================== third interface board: === */ +#define CDROM_PORT 0x630 /* <-----------<< port address */ +#define SBPRO 1 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-4) /* ==================== fourth interface board: === */ +#define CDROM_PORT 0x634 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */ +#endif + +/* + * some more or less user dependent definitions - service them! + */ + +/* Set this to 0 once you have configured your interface definitions right. */ +#define DISTRIBUTION 1 + +/* + * Time to wait after giving a message. + * This gets important if you enable non-standard DBG_xxx flags. + * You will see what happens if you omit the pause or make it + * too short. Be warned! + */ +#define KLOGD_PAUSE 1 + +/* tray control: eject tray if no disk is in */ +#if DISTRIBUTION +#define JUKEBOX 0 +#else +#define JUKEBOX 1 +#endif DISTRIBUTION + +/* tray control: eject tray after last use */ +#if DISTRIBUTION +#define EJECT 0 +#else +#define EJECT 1 +#endif DISTRIBUTION + +/* max. number of audio frames to read with one */ +/* request (allocates n* 2352 bytes kernel memory!) */ +/* may be freely adjusted, f.e. 75 (= 1 sec.), at */ +/* runtime by use of the CDROMAUDIOBUFSIZ ioctl. */ +#define READ_AUDIO 0 + +/* Optimizations for the Teac CD-55A drive read performance. + * SBP_TEAC_SPEED can be changed here, or one can set the + * variable "teac" when loading as a module. + * Valid settings are: + * 0 - very slow - the recommended "DISTRIBUTION 1" setup. + * 1 - 2x performance with little overhead. No busy waiting. + * 2 - 4x performance with 5ms overhead per read. Busy wait. + * + * Setting SBP_TEAC_SPEED or the variable 'teac' to anything + * other than 0 may cause problems. If you run into them, first + * change SBP_TEAC_SPEED back to 0 and see if your drive responds + * normally. If yes, you are "allowed" to report your case - to help + * me with the driver, not to solve your hassle. Don´t mail if you + * simply are stuck into your own "tuning" experiments, you know? + */ +#define SBP_TEAC_SPEED 1 + +/*==========================================================================*/ +/*==========================================================================*/ +/* + * nothing to change below here if you are not fully aware what you're doing + */ +#ifndef _LINUX_SBPCD_H + +#define _LINUX_SBPCD_H +/*==========================================================================*/ +/*==========================================================================*/ +/* + * driver's own read_ahead, data mode + */ +#define SBP_BUFFER_FRAMES 8 + +#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */ +#undef FUTURE +#undef SAFE_MIXED + +#define TEST_UPC 0 +#define SPEA_TEST 0 +#define TEST_STI 0 +#define OLD_BUSY 0 +#undef PATH_CHECK +#ifndef SOUND_BASE +#define SOUND_BASE 0 +#endif +#if DISTRIBUTION +#undef SBP_TEAC_SPEED +#define SBP_TEAC_SPEED 0 +#endif +/*==========================================================================*/ +/* + * DDI interface definitions + * "invented" by Fred N. van Kempen.. + */ +#define DDIOCSDBG 0x9000 + +/*==========================================================================*/ +/* + * "private" IOCTL functions + */ +#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ + +/*==========================================================================*/ +/* + * Debug output levels + */ +#define DBG_INF 1 /* necessary information */ +#define DBG_BSZ 2 /* BLOCK_SIZE trace */ +#define DBG_REA 3 /* READ status trace */ +#define DBG_CHK 4 /* MEDIA CHECK trace */ +#define DBG_TIM 5 /* datarate timer test */ +#define DBG_INI 6 /* initialization trace */ +#define DBG_TOC 7 /* tell TocEntry values */ +#define DBG_IOC 8 /* ioctl trace */ +#define DBG_STA 9 /* ResponseStatus() trace */ +#define DBG_ERR 10 /* cc_ReadError() trace */ +#define DBG_CMD 11 /* cmd_out() trace */ +#define DBG_WRN 12 /* give explanation before auto-probing */ +#define DBG_MUL 13 /* multi session code test */ +#define DBG_IDX 14 /* test code for drive_id !=0 */ +#define DBG_IOX 15 /* some special information */ +#define DBG_DID 16 /* drive ID test */ +#define DBG_RES 17 /* drive reset info */ +#define DBG_SPI 18 /* SpinUp test */ +#define DBG_IOS 19 /* ioctl trace: subchannel functions */ +#define DBG_IO2 20 /* ioctl trace: general */ +#define DBG_UPC 21 /* show UPC information */ +#define DBG_XA1 22 /* XA mode debugging */ +#define DBG_LCK 23 /* door (un)lock info */ +#define DBG_SQ1 24 /* dump SubQ frame */ +#define DBG_AUD 25 /* READ AUDIO debugging */ +#define DBG_SEQ 26 /* Sequoia interface configuration trace */ +#define DBG_LCS 27 /* Longshine LCS-7260 debugging trace */ +#define DBG_CD2 28 /* MKE/Funai CD200 debugging trace */ +#define DBG_TEA 29 /* TEAC CD-55A debugging trace */ +#define DBG_ECS 30 /* ECS-AT (Vertos 100) debugging trace */ +#define DBG_000 31 /* unnecessary information */ + +/*==========================================================================*/ +/*==========================================================================*/ + +/* + * bits of flags_cmd_out: + */ +#define f_respo3 0x100 +#define f_putcmd 0x80 +#define f_respo2 0x40 +#define f_lopsta 0x20 +#define f_getsta 0x10 +#define f_ResponseStatus 0x08 +#define f_obey_p_check 0x04 +#define f_bit1 0x02 +#define f_wait_if_busy 0x01 + +/* + * diskstate_flags: + */ +#define x80_bit 0x80 +#define upc_bit 0x40 +#define volume_bit 0x20 +#define toc_bit 0x10 +#define multisession_bit 0x08 +#define cd_size_bit 0x04 +#define subq_bit 0x02 +#define frame_size_bit 0x01 + +/* + * disk states (bits of diskstate_flags): + */ +#define upc_valid (D_S[d].diskstate_flags&upc_bit) +#define volume_valid (D_S[d].diskstate_flags&volume_bit) +#define toc_valid (D_S[d].diskstate_flags&toc_bit) +#define cd_size_valid (D_S[d].diskstate_flags&cd_size_bit) +#define subq_valid (D_S[d].diskstate_flags&subq_bit) +#define frame_size_valid (D_S[d].diskstate_flags&frame_size_bit) + +/* + * the status_bits variable + */ +#define p_success 0x100 +#define p_door_closed 0x80 +#define p_caddy_in 0x40 +#define p_spinning 0x20 +#define p_check 0x10 +#define p_busy_new 0x08 +#define p_door_locked 0x04 +#define p_disk_ok 0x01 + +/* + * LCS-7260 special status result bits: + */ +#define p_lcs_door_locked 0x02 +#define p_lcs_door_closed 0x01 /* probably disk_in */ + +/* + * CR-52x special status result bits: + */ +#define p_caddin_old 0x40 +#define p_success_old 0x08 +#define p_busy_old 0x04 +#define p_bit_1 0x02 /* hopefully unused now */ + +/* + * "generation specific" defs of the status result bits: + */ +#define p0_door_closed 0x80 +#define p0_caddy_in 0x40 +#define p0_spinning 0x20 +#define p0_check 0x10 +#define p0_success 0x08 /* unused */ +#define p0_busy 0x04 +#define p0_bit_1 0x02 /* unused */ +#define p0_disk_ok 0x01 + +#define pL_disk_in 0x40 +#define pL_spinning 0x20 +#define pL_check 0x10 +#define pL_success 0x08 /* unused ?? */ +#define pL_busy 0x04 +#define pL_door_locked 0x02 +#define pL_door_closed 0x01 + +#define pV_door_closed 0x40 +#define pV_spinning 0x20 +#define pV_check 0x10 +#define pV_success 0x08 +#define pV_busy 0x04 +#define pV_door_locked 0x02 +#define pV_disk_ok 0x01 + +#define p1_door_closed 0x80 +#define p1_disk_in 0x40 +#define p1_spinning 0x20 +#define p1_check 0x10 +#define p1_busy 0x08 +#define p1_door_locked 0x04 +#define p1_bit_1 0x02 /* unused */ +#define p1_disk_ok 0x01 + +#define p2_disk_ok 0x80 +#define p2_door_locked 0x40 +#define p2_spinning 0x20 +#define p2_busy2 0x10 +#define p2_busy1 0x08 +#define p2_door_closed 0x04 +#define p2_disk_in 0x02 +#define p2_check 0x01 + +/* + * used drive states: + */ +#define st_door_closed (D_S[d].status_bits&p_door_closed) +#define st_caddy_in (D_S[d].status_bits&p_caddy_in) +#define st_spinning (D_S[d].status_bits&p_spinning) +#define st_check (D_S[d].status_bits&p_check) +#define st_busy (D_S[d].status_bits&p_busy_new) +#define st_door_locked (D_S[d].status_bits&p_door_locked) +#define st_diskok (D_S[d].status_bits&p_disk_ok) + +/* + * bits of the CDi_status register: + */ +#define s_not_result_ready 0x04 /* 0: "result ready" */ +#define s_not_data_ready 0x02 /* 0: "data ready" */ +#define s_attention 0x01 /* 1: "attention required" */ +/* + * usable as: + */ +#define DRV_ATTN ((inb(CDi_status)&s_attention)!=0) +#define DATA_READY ((inb(CDi_status)&s_not_data_ready)==0) +#define RESULT_READY ((inb(CDi_status)&s_not_result_ready)==0) + +/* + * drive families and types (firmware versions): + */ +#define drv_fam0 0x0100 /* CR-52x family */ +#define drv_199 (drv_fam0+0x01) /* <200 */ +#define drv_200 (drv_fam0+0x02) /* <201 */ +#define drv_201 (drv_fam0+0x03) /* <210 */ +#define drv_210 (drv_fam0+0x04) /* <211 */ +#define drv_211 (drv_fam0+0x05) /* <300 */ +#define drv_300 (drv_fam0+0x06) /* >=300 */ + +#define drv_fam1 0x0200 /* CR-56x family */ +#define drv_099 (drv_fam1+0x01) /* <100 */ +#define drv_100 (drv_fam1+0x02) /* >=100, only 1.02 and 5.00 known */ + +#define drv_fam2 0x0400 /* CD200 family */ + +#define drv_famT 0x0800 /* TEAC CD-55A */ + +#define drv_famL 0x1000 /* Longshine family */ +#define drv_260 (drv_famL+0x01) /* LCS-7260 */ +#define drv_e1 (drv_famL+0x01) /* LCS-7260, firmware "A E1" */ +#define drv_f4 (drv_famL+0x02) /* LCS-7260, firmware "A4F4" */ + +#define drv_famV 0x2000 /* ECS-AT (vertos-100) family */ +#define drv_at (drv_famV+0x01) /* ECS-AT, firmware "1.00" */ + +#define fam0_drive (D_S[d].drv_type&drv_fam0) +#define famL_drive (D_S[d].drv_type&drv_famL) +#define famV_drive (D_S[d].drv_type&drv_famV) +#define fam1_drive (D_S[d].drv_type&drv_fam1) +#define fam2_drive (D_S[d].drv_type&drv_fam2) +#define famT_drive (D_S[d].drv_type&drv_famT) +#define fam0L_drive (D_S[d].drv_type&(drv_fam0|drv_famL)) +#define fam0V_drive (D_S[d].drv_type&(drv_fam0|drv_famV)) +#define famLV_drive (D_S[d].drv_type&(drv_famL|drv_famV)) +#define fam0LV_drive (D_S[d].drv_type&(drv_fam0|drv_famL|drv_famV)) +#define fam1L_drive (D_S[d].drv_type&(drv_fam1|drv_famL)) +#define fam1V_drive (D_S[d].drv_type&(drv_fam1|drv_famV)) +#define fam1LV_drive (D_S[d].drv_type&(drv_fam1|drv_famL|drv_famV)) +#define fam01_drive (D_S[d].drv_type&(drv_fam0|drv_fam1)) +#define fam12_drive (D_S[d].drv_type&(drv_fam1|drv_fam2)) +#define fam2T_drive (D_S[d].drv_type&(drv_fam2|drv_famT)) + +/* + * audio states: + */ +#define audio_playing 2 +#define audio_pausing 1 + +/* + * drv_pattern, drv_options: + */ +#define speed_auto 0x80 +#define speed_300 0x40 +#define speed_150 0x20 +#define audio_mono 0x04 + +/* + * values of cmd_type (0 else): + */ +#define READ_M1 0x01 /* "data mode 1": 2048 bytes per frame */ +#define READ_M2 0x02 /* "data mode 2": 12+2048+280 bytes per frame */ +#define READ_SC 0x04 /* "subchannel info": 96 bytes per frame */ +#define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */ + +/* + * sense_byte: + * + * values: 00 + * 01 + * 81 + * 82 "raw audio" mode + * xx from infobuf[0] after 85 00 00 00 00 00 00 + */ + +/* audio status (bin) */ +#define aud_00 0x00 /* Audio status byte not supported or not valid */ +#define audx11 0x0b /* Audio play operation in progress */ +#define audx12 0x0c /* Audio play operation paused */ +#define audx13 0x0d /* Audio play operation successfully completed */ +#define audx14 0x0e /* Audio play operation stopped due to error */ +#define audx15 0x0f /* No current audio status to return */ +/* audio status (bcd) */ +#define aud_11 0x11 /* Audio play operation in progress */ +#define aud_12 0x12 /* Audio play operation paused */ +#define aud_13 0x13 /* Audio play operation successfully completed */ +#define aud_14 0x14 /* Audio play operation stopped due to error */ +#define aud_15 0x15 /* No current audio status to return */ + +/* + * highest allowed drive number (MINOR+1) + */ +#define NR_SBPCD 4 + +/* + * we try to never disable interrupts - seems to work + */ +#define SBPCD_DIS_IRQ 0 + +/* + * "write byte to port" + */ +#define OUT(x,y) outb(y,x) + +/*==========================================================================*/ + +#define MIXER_addr SOUND_BASE+4 /* sound card's address register */ +#define MIXER_data SOUND_BASE+5 /* sound card's data register */ +#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */ + +/*==========================================================================*/ + +#define MAX_TRACKS 99 + +#define ERR_DISKCHANGE 615 + +/*==========================================================================*/ +/* + * To make conversions easier (machine dependent!) + */ +typedef union _msf +{ + u_int n; + u_char c[4]; +} MSF; + +typedef union _blk +{ + u_int n; + u_char c[4]; +} BLK; + +/*==========================================================================*/ + +/*============================================================================ +============================================================================== + +COMMAND SET of "old" drives like CR-521, CR-522 + (the CR-562 family is different): + +No. Command Code +-------------------------------------------- + +Drive Commands: + 1 Seek 01 + 2 Read Data 02 + 3 Read XA-Data 03 + 4 Read Header 04 + 5 Spin Up 05 + 6 Spin Down 06 + 7 Diagnostic 07 + 8 Read UPC 08 + 9 Read ISRC 09 +10 Play Audio 0A +11 Play Audio MSF 0B +12 Play Audio Track/Index 0C + +Status Commands: +13 Read Status 81 +14 Read Error 82 +15 Read Drive Version 83 +16 Mode Select 84 +17 Mode Sense 85 +18 Set XA Parameter 86 +19 Read XA Parameter 87 +20 Read Capacity 88 +21 Read SUB_Q 89 +22 Read Disc Code 8A +23 Read Disc Information 8B +24 Read TOC 8C +25 Pause/Resume 8D +26 Read Packet 8E +27 Read Path Check 00 + + +all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first + +mnemo 7-byte command #bytes response (r0...rn) +________ ____________________ ____ + +Read Status: +status: 81. (1) one-byte command, gives the main + status byte +Read Error: +check1: 82 00 00 00 00 00 00. (6) r1: audio status + +Read Packet: +check2: 8e xx 00 00 00 00 00. (xx) gets xx bytes response, relating + to commands 01 04 05 07 08 09 + +Play Audio: +play: 0a ll-bb-aa nn-nn-nn. (0) play audio, ll-bb-aa: starting block (lba), + nn-nn-nn: #blocks +Play Audio MSF: + 0b mm-ss-ff mm-ss-ff (0) play audio from/to + +Play Audio Track/Index: + 0c ... + +Pause/Resume: +pause: 8d pr 00 00 00 00 00. (0) pause (pr=00) + resume (pr=80) audio playing + +Mode Select: + 84 00 nn-nn ??-?? 00 (0) nn-nn: 2048 or 2340 + possibly defines transfer size + +set_vol: 84 83 00 00 sw le 00. (0) sw(itch): lrxxxxxx (off=1) + le(vel): min=0, max=FF, else half + (firmware 2.11) + +Mode Sense: +get_vol: 85 03 00 00 00 00 00. (2) tell current audio volume setting + +Read Disc Information: +tocdesc: 8b 00 00 00 00 00 00. (6) read the toc descriptor ("msf-bin"-format) + +Read TOC: +tocent: 8c fl nn 00 00 00 00. (8) read toc entry #nn + (fl=0:"lba"-, =2:"msf-bin"-format) + +Read Capacity: +capacit: 88 00 00 00 00 00 00. (5) "read CD-ROM capacity" + + +Read Path Check: +ping: 00 00 00 00 00 00 00. (2) r0=AA, r1=55 + ("ping" if the drive is connected) + +Read Drive Version: +ident: 83 00 00 00 00 00 00. (12) gives "MATSHITAn.nn" + (n.nn = 2.01, 2.11., 3.00, ...) + +Seek: +seek: 01 00 ll-bb-aa 00 00. (0) +seek: 01 02 mm-ss-ff 00 00. (0) + +Read Data: +read: 02 xx-xx-xx nn-nn fl. (??) read nn-nn blocks of 2048 bytes, + starting at block xx-xx-xx + fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx + +Read XA-Data: +read: 03 xx-xx-xx nn-nn fl. (??) read nn-nn blocks of 2340 bytes, + starting at block xx-xx-xx + fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx + +Read SUB_Q: + 89 fl 00 00 00 00 00. (13) r0: audio status, r4-r7: lba/msf, + fl=0: "lba", fl=2: "msf" + +Read Disc Code: + 8a 00 00 00 00 00 00. (14) possibly extended "check condition"-info + +Read Header: + 04 00 ll-bb-aa 00 00. (0) 4 bytes response with "check2" + 04 02 mm-ss-ff 00 00. (0) 4 bytes response with "check2" + +Spin Up: + 05 00 ll-bb-aa 00 00. (0) possibly implies a "seek" + +Spin Down: + 06 ... + +Diagnostic: + 07 00 ll-bb-aa 00 00. (2) 2 bytes response with "check2" + 07 02 mm-ss-ff 00 00. (2) 2 bytes response with "check2" + +Read UPC: + 08 00 ll-bb-aa 00 00. (16) + 08 02 mm-ss-ff 00 00. (16) + +Read ISRC: + 09 00 ll-bb-aa 00 00. (15) 15 bytes response with "check2" + 09 02 mm-ss-ff 00 00. (15) 15 bytes response with "check2" + +Set XA Parameter: + 86 ... + +Read XA Parameter: + 87 ... + +============================================================================== +============================================================================*/ + +/* + * commands + * + * CR-52x: CMD0_ + * CR-56x: CMD1_ + * CD200: CMD2_ + * LCS-7260: CMDL_ + * TEAC CD-55A: CMDT_ + * ECS-AT: CMDV_ + */ +#define CMD1_RESET 0x0a +#define CMD2_RESET 0x01 +#define CMDT_RESET 0xc0 + +#define CMD1_LOCK_CTL 0x0c +#define CMD2_LOCK_CTL 0x1e +#define CMDT_LOCK_CTL CMD2_LOCK_CTL +#define CMDL_LOCK_CTL 0x0e +#define CMDV_LOCK_CTL CMDL_LOCK_CTL + +#define CMD1_TRAY_CTL 0x07 +#define CMD2_TRAY_CTL 0x1b +#define CMDT_TRAY_CTL CMD2_TRAY_CTL +#define CMDL_TRAY_CTL 0x0d +#define CMDV_TRAY_CTL CMDL_TRAY_CTL + +#define CMD1_MULTISESS 0x8d +#define CMDL_MULTISESS 0x8c +#define CMDV_MULTISESS CMDL_MULTISESS + +#define CMD1_SUBCHANINF 0x11 +#define CMD2_SUBCHANINF 0x?? + +#define CMD1_ABORT 0x08 +#define CMD2_ABORT 0x08 +#define CMDT_ABORT 0x08 + +#define CMD2_x02 0x02 + +#define CMD2_SETSPEED 0xda + +#define CMD0_PATH_CHECK 0x00 +#define CMD1_PATH_CHECK 0x??? +#define CMD2_PATH_CHECK 0x??? +#define CMDT_PATH_CHECK 0x??? +#define CMDL_PATH_CHECK CMD0_PATH_CHECK +#define CMDV_PATH_CHECK CMD0_PATH_CHECK + +#define CMD0_SEEK 0x01 +#define CMD1_SEEK CMD0_SEEK +#define CMD2_SEEK 0x2b +#define CMDT_SEEK CMD2_SEEK +#define CMDL_SEEK CMD0_SEEK +#define CMDV_SEEK CMD0_SEEK + +#define CMD0_READ 0x02 +#define CMD1_READ 0x10 +#define CMD2_READ 0x28 +#define CMDT_READ CMD2_READ +#define CMDL_READ CMD0_READ +#define CMDV_READ CMD0_READ + +#define CMD0_READ_XA 0x03 +#define CMD2_READ_XA 0xd4 +#define CMD2_READ_XA2 0xd5 +#define CMDL_READ_XA CMD0_READ_XA /* really ?? */ +#define CMDV_READ_XA CMD0_READ_XA + +#define CMD0_READ_HEAD 0x04 + +#define CMD0_SPINUP 0x05 +#define CMD1_SPINUP 0x02 +#define CMD2_SPINUP CMD2_TRAY_CTL +#define CMDL_SPINUP CMD0_SPINUP +#define CMDV_SPINUP CMD0_SPINUP + +#define CMD0_SPINDOWN 0x06 /* really??? */ +#define CMD1_SPINDOWN 0x06 +#define CMD2_SPINDOWN CMD2_TRAY_CTL +#define CMDL_SPINDOWN 0x0d +#define CMDV_SPINDOWN CMD0_SPINDOWN + +#define CMD0_DIAG 0x07 + +#define CMD0_READ_UPC 0x08 +#define CMD1_READ_UPC 0x88 +#define CMD2_READ_UPC 0x??? +#define CMDL_READ_UPC CMD0_READ_UPC +#define CMDV_READ_UPC 0x8f + +#define CMD0_READ_ISRC 0x09 + +#define CMD0_PLAY 0x0a +#define CMD1_PLAY 0x??? +#define CMD2_PLAY 0x??? +#define CMDL_PLAY CMD0_PLAY +#define CMDV_PLAY CMD0_PLAY + +#define CMD0_PLAY_MSF 0x0b +#define CMD1_PLAY_MSF 0x0e +#define CMD2_PLAY_MSF 0x47 +#define CMDT_PLAY_MSF CMD2_PLAY_MSF +#define CMDL_PLAY_MSF 0x??? + +#define CMD0_PLAY_TI 0x0c +#define CMD1_PLAY_TI 0x0f + +#define CMD0_STATUS 0x81 +#define CMD1_STATUS 0x05 +#define CMD2_STATUS 0x00 +#define CMDT_STATUS CMD2_STATUS +#define CMDL_STATUS CMD0_STATUS +#define CMDV_STATUS CMD0_STATUS +#define CMD2_SEEK_LEADIN 0x00 + +#define CMD0_READ_ERR 0x82 +#define CMD1_READ_ERR CMD0_READ_ERR +#define CMD2_READ_ERR 0x03 +#define CMDT_READ_ERR CMD2_READ_ERR /* get audio status */ +#define CMDL_READ_ERR CMD0_READ_ERR +#define CMDV_READ_ERR CMD0_READ_ERR + +#define CMD0_READ_VER 0x83 +#define CMD1_READ_VER CMD0_READ_VER +#define CMD2_READ_VER 0x12 +#define CMDT_READ_VER CMD2_READ_VER /* really ?? */ +#define CMDL_READ_VER CMD0_READ_VER +#define CMDV_READ_VER CMD0_READ_VER + +#define CMD0_SETMODE 0x84 +#define CMD1_SETMODE 0x09 +#define CMD2_SETMODE 0x55 +#define CMDT_SETMODE CMD2_SETMODE +#define CMDL_SETMODE CMD0_SETMODE + +#define CMD0_GETMODE 0x85 +#define CMD1_GETMODE 0x84 +#define CMD2_GETMODE 0x5a +#define CMDT_GETMODE CMD2_GETMODE +#define CMDL_GETMODE CMD0_GETMODE + +#define CMD0_SET_XA 0x86 + +#define CMD0_GET_XA 0x87 + +#define CMD0_CAPACITY 0x88 +#define CMD1_CAPACITY 0x85 +#define CMD2_CAPACITY 0x25 +#define CMDL_CAPACITY CMD0_CAPACITY /* missing in some firmware versions */ + +#define CMD0_READSUBQ 0x89 +#define CMD1_READSUBQ 0x87 +#define CMD2_READSUBQ 0x42 +#define CMDT_READSUBQ CMD2_READSUBQ +#define CMDL_READSUBQ CMD0_READSUBQ +#define CMDV_READSUBQ CMD0_READSUBQ + +#define CMD0_DISKCODE 0x8a + +#define CMD0_DISKINFO 0x8b +#define CMD1_DISKINFO CMD0_DISKINFO +#define CMD2_DISKINFO 0x43 +#define CMDT_DISKINFO CMD2_DISKINFO +#define CMDL_DISKINFO CMD0_DISKINFO +#define CMDV_DISKINFO CMD0_DISKINFO + +#define CMD0_READTOC 0x8c +#define CMD1_READTOC CMD0_READTOC +#define CMD2_READTOC 0x??? +#define CMDL_READTOC CMD0_READTOC +#define CMDV_READTOC CMD0_READTOC + +#define CMD0_PAU_RES 0x8d +#define CMD1_PAU_RES 0x0d +#define CMD2_PAU_RES 0x4b +#define CMDT_PAUSE CMD2_PAU_RES +#define CMDL_PAU_RES CMD0_PAU_RES +#define CMDV_PAUSE CMD0_PAU_RES + +#define CMD0_PACKET 0x8e +#define CMD1_PACKET CMD0_PACKET +#define CMD2_PACKET 0x??? +#define CMDL_PACKET CMD0_PACKET +#define CMDV_PACKET 0x??? + +/*==========================================================================*/ +/*==========================================================================*/ +#endif _LINUX_SBPCD_H +/*==========================================================================*/ +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff --git a/drivers/cdrom/sjcd.h b/drivers/cdrom/sjcd.h new file mode 100644 index 000000000..0aa5e7146 --- /dev/null +++ b/drivers/cdrom/sjcd.h @@ -0,0 +1,181 @@ +/* + * Definitions for a Sanyo CD-ROM interface. + * + * Copyright (C) 1995 Vadim V. Model + * model@cecmow.enet.dec.com + * vadim@rbrf.msk.su + * vadim@ipsun.ras.ru + * Eric van der Maarel + * H.T.M.v.d.Maarel@marin.nl + * + * This information is based on mcd.c from M. Harriss and sjcd102.lst from + * E. Moenkeberg. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SJCD_H__ +#define __SJCD_H__ + +/* + * Change this to set the I/O port address as default. More flexibility + * come with setup implementation. + */ +#define SJCD_BASE_ADDR 0x340 + +/* + * Change this to set the irq as default. Really SANYO do not use interrupts + * at all. + */ +#define SJCD_INTR_NR 0 + +/* + * Change this to set the dma as default value. really SANYO does not use + * direct memory access at all. + */ +#define SJCD_DMA_NR 0 + +/* + * Macros which allow us to find out the status of the drive. + */ +#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0) +#define SJCD_DATA_AVAILABLE( x ) (((x)&0x01)==0) + +/* + * Port access macro. Three ports are available: S-data port (command port), + * status port (read only) and D-data port (read only). + */ +#define SJCDPORT( x ) ( sjcd_base + ( x ) ) +#define SJCD_STATUS_PORT SJCDPORT( 1 ) +#define SJCD_S_DATA_PORT SJCDPORT( 0 ) +#define SJCD_COMMAND_PORT SJCDPORT( 0 ) +#define SJCD_D_DATA_PORT SJCDPORT( 2 ) + +/* + * Drive info bits. Drive info available as first (mandatory) byte of + * command completion status. + */ +#define SST_NOT_READY 0x10 /* no disk in the drive (???) */ +#define SST_MEDIA_CHANGED 0x20 /* disk is changed */ +#define SST_DOOR_OPENED 0x40 /* door is open */ + +/* commands */ + +#define SCMD_EJECT_TRAY 0xD0 /* eject tray if not locked */ +#define SCMD_LOCK_TRAY 0xD2 /* lock tray when in */ +#define SCMD_UNLOCK_TRAY 0xD4 /* unlock tray when in */ +#define SCMD_CLOSE_TRAY 0xD6 /* load tray in */ + +#define SCMD_RESET 0xFA /* soft reset */ +#define SCMD_GET_STATUS 0x80 +#define SCMD_GET_VERSION 0xCC + +#define SCMD_DATA_READ 0xA0 /* are the same, depend on mode&args */ +#define SCMD_SEEK 0xA0 +#define SCMD_PLAY 0xA0 + +#define SCMD_GET_QINFO 0xA8 + +#define SCMD_SET_MODE 0xC4 +#define SCMD_MODE_PLAY 0xE0 +#define SCMD_MODE_COOKED (0xF8 & ~0x20) +#define SCMD_MODE_RAW 0xF9 +#define SCMD_MODE_x20_BIT 0x20 /* What is it for ? */ + +#define SCMD_SET_VOLUME 0xAE +#define SCMD_PAUSE 0xE0 +#define SCMD_STOP 0xE0 + +#define SCMD_GET_DISK_INFO 0xAA + +/* + * Some standard arguments for SCMD_GET_DISK_INFO. + */ +#define SCMD_GET_1_TRACK 0xA0 /* get the first track information */ +#define SCMD_GET_L_TRACK 0xA1 /* get the last track information */ +#define SCMD_GET_D_SIZE 0xA2 /* get the whole disk information */ + +/* + * Borrowed from hd.c. Allows to optimize multiple port read commands. + */ +#define S_READ_DATA( port, buf, nr ) insb( port, buf, nr ) + +/* + * We assume that there are no audio disks with TOC length more than this + * number (I personally have never seen disks with more than 20 fragments). + */ +#define SJCD_MAX_TRACKS 100 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct sjcd_hw_disk_info { + unsigned char track_control; + unsigned char track_no; + unsigned char x, y, z; + union { + unsigned char track_no; + struct msf track_msf; + } un; +}; + +struct sjcd_hw_qinfo { + unsigned char track_control; + unsigned char track_no; + unsigned char x; + struct msf rel; + struct msf abs; +}; + +struct sjcd_play_msf { + struct msf start; + struct msf end; +}; + +struct sjcd_disk_info { + unsigned char first; + unsigned char last; + struct msf disk_length; + struct msf first_track; +}; + +struct sjcd_toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char point_index; + struct msf track_time; + struct msf disk_time; +}; + +#if defined( SJCD_GATHER_STAT ) + +struct sjcd_stat { + int ticks; + int tticks[ 8 ]; + int idle_ticks; + int start_ticks; + int mode_ticks; + int read_ticks; + int data_ticks; + int stop_ticks; + int stopping_ticks; +}; + +#endif + +#endif diff --git a/drivers/cdrom/sonycd535.h b/drivers/cdrom/sonycd535.h new file mode 100644 index 000000000..5dea1ef16 --- /dev/null +++ b/drivers/cdrom/sonycd535.h @@ -0,0 +1,183 @@ +#ifndef SONYCD535_H +#define SONYCD535_H + +/* + * define all the commands recognized by the CDU-531/5 + */ +#define SONY535_REQUEST_DRIVE_STATUS_1 (0x80) +#define SONY535_REQUEST_SENSE (0x82) +#define SONY535_REQUEST_DRIVE_STATUS_2 (0x84) +#define SONY535_REQUEST_ERROR_STATUS (0x86) +#define SONY535_REQUEST_AUDIO_STATUS (0x88) +#define SONY535_INQUIRY (0x8a) + +#define SONY535_SET_INACTIVITY_TIME (0x90) + +#define SONY535_SEEK_AND_READ_N_BLOCKS_1 (0xa0) +#define SONY535_SEEK_AND_READ_N_BLOCKS_2 (0xa4) +#define SONY535_PLAY_AUDIO (0xa6) + +#define SONY535_REQUEST_DISC_CAPACITY (0xb0) +#define SONY535_REQUEST_TOC_DATA (0xb2) +#define SONY535_REQUEST_SUB_Q_DATA (0xb4) +#define SONY535_REQUEST_ISRC (0xb6) +#define SONY535_REQUEST_UPC_EAN (0xb8) + +#define SONY535_SET_DRIVE_MODE (0xc0) +#define SONY535_REQUEST_DRIVE_MODE (0xc2) +#define SONY535_SET_RETRY_COUNT (0xc4) + +#define SONY535_DIAGNOSTIC_1 (0xc6) +#define SONY535_DIAGNOSTIC_4 (0xcc) +#define SONY535_DIAGNOSTIC_5 (0xce) + +#define SONY535_EJECT_CADDY (0xd0) +#define SONY535_DISABLE_EJECT_BUTTON (0xd2) +#define SONY535_ENABLE_EJECT_BUTTON (0xd4) + +#define SONY535_HOLD (0xe0) +#define SONY535_AUDIO_PAUSE_ON_OFF (0xe2) +#define SONY535_SET_VOLUME (0xe8) + +#define SONY535_STOP (0xf0) +#define SONY535_SPIN_UP (0xf2) +#define SONY535_SPIN_DOWN (0xf4) + +#define SONY535_CLEAR_PARAMETERS (0xf6) +#define SONY535_CLEAR_ENDING_ADDRESS (0xf8) + +/* + * define some masks + */ +#define SONY535_DATA_NOT_READY_BIT (0x1) +#define SONY535_RESULT_NOT_READY_BIT (0x2) + +/* + * drive status 1 + */ +#define SONY535_STATUS1_COMMAND_ERROR (0x1) +#define SONY535_STATUS1_DATA_ERROR (0x2) +#define SONY535_STATUS1_SEEK_ERROR (0x4) +#define SONY535_STATUS1_DISC_TYPE_ERROR (0x8) +#define SONY535_STATUS1_NOT_SPINNING (0x10) +#define SONY535_STATUS1_EJECT_BUTTON_PRESSED (0x20) +#define SONY535_STATUS1_CADDY_NOT_INSERTED (0x40) +#define SONY535_STATUS1_BYTE_TWO_FOLLOWS (0x80) + +/* + * drive status 2 + */ +#define SONY535_CDD_LOADING_ERROR (0x7) +#define SONY535_CDD_NO_DISC (0x8) +#define SONY535_CDD_UNLOADING_ERROR (0x9) +#define SONY535_CDD_CADDY_NOT_INSERTED (0xd) +#define SONY535_ATN_RESET_OCCURRED (0x2) +#define SONY535_ATN_DISC_CHANGED (0x4) +#define SONY535_ATN_RESET_AND_DISC_CHANGED (0x6) +#define SONY535_ATN_EJECT_IN_PROGRESS (0xe) +#define SONY535_ATN_BUSY (0xf) + +/* + * define some parameters + */ +#define SONY535_AUDIO_DRIVE_MODE (0) +#define SONY535_CDROM_DRIVE_MODE (0xe0) + +#define SONY535_PLAY_OP_PLAYBACK (0) +#define SONY535_PLAY_OP_ENTER_HOLD (1) +#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR (2) +#define SONY535_PLAY_OP_SCAN_FORWARD (3) +#define SONY535_PLAY_OP_SCAN_BACKWARD (4) + +/* + * convert from msf format to block number + */ +#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f)) +#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2]) + +/* + * error return values from the doSonyCmd() routines + */ +#define TIME_OUT (-1) +#define NO_CDROM (-2) +#define BAD_STATUS (-3) +#define CD_BUSY (-4) +#define NOT_DATA_CD (-5) +#define NO_ROOM (-6) + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_JIFFIES_TIMEOUT (5*HZ) /* Maximum time + the drive will wait/try for an + operation */ +#define SONY_READY_RETRIES (50000) /* How many times to retry a + spin waiting for a register + to come ready */ +#define SONY535_FAST_POLLS (10000) /* how many times recheck + status waiting for a data + to become ready */ + +typedef unsigned char Byte; + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s535_sony_drive_config +{ + char vendor_id[8]; + char product_id[16]; + char product_rev_level[4]; +}; + +/* The following is returned from the request sub-q data command */ +struct s535_sony_subcode +{ + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char abs_msf[3]; +}; + +struct s535_sony_disc_capacity +{ + Byte mFirstTrack, sFirstTrack, fFirstTrack; + Byte mLeadOut, sLeadOut, fLeadOut; +}; + +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s535_sony_toc +{ + unsigned char reserved0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char reserved0a; + unsigned char reserved0b; + unsigned char reserved1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char reserved2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char reserved :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[100]; + + unsigned int lead_out_start_lba; +}; + +#endif /* SONYCD535_H */ diff --git a/drivers/char/cd1865.h b/drivers/char/cd1865.h new file mode 100644 index 000000000..e7407f4b8 --- /dev/null +++ b/drivers/char/cd1865.h @@ -0,0 +1,263 @@ +/* + * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865 + * for the Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + */ + +/* + * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards. + */ + + +/* Values of choice for Interrupt ACKs */ +/* These values are "obligatory" if you use the register based + * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865 + * databook */ +#define SX_ACK_MINT 0x75 /* goes to PILR1 */ +#define SX_ACK_TINT 0x76 /* goes to PILR2 */ +#define SX_ACK_RINT 0x77 /* goes to PILR3 */ + +/* Chip ID (is used when chips ar daisy chained.) */ +#define SX_ID 0x10 + +/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */ + +#define CD186x_NCH 8 /* Total number of channels */ +#define CD186x_TPC 16 /* Ticks per character */ +#define CD186x_NFIFO 8 /* TX FIFO size */ + + +/* Global registers */ + +#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */ +#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */ +#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */ +#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */ +#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */ +#define CD186x_CAR 0x64 /* Channel Access Register */ +#define CD186x_SRSR 0x65 /* Channel Access Register */ +#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */ +#define CD186x_PPRH 0x70 /* Prescaler Period Register High */ +#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */ +#define CD186x_RDR 0x78 /* Receiver Data Register */ +#define CD186x_RCSR 0x7a /* Receiver Character Status Register */ +#define CD186x_TDR 0x7b /* Transmit Data Register */ +#define CD186x_EOIR 0x7f /* End of Interrupt Register */ +#define CD186x_MRAR 0x75 /* Modem Request Acknowlege register */ +#define CD186x_TRAR 0x76 /* Transmit Request Acknowlege register */ +#define CD186x_RRAR 0x77 /* Recieve Request Acknowlege register */ +#define CD186x_SRCR 0x66 /* Service Request Configuration register */ + +/* Channel Registers */ + +#define CD186x_CCR 0x01 /* Channel Command Register */ +#define CD186x_IER 0x02 /* Interrupt Enable Register */ +#define CD186x_COR1 0x03 /* Channel Option Register 1 */ +#define CD186x_COR2 0x04 /* Channel Option Register 2 */ +#define CD186x_COR3 0x05 /* Channel Option Register 3 */ +#define CD186x_CCSR 0x06 /* Channel Control Status Register */ +#define CD186x_RDCR 0x07 /* Receive Data Count Register */ +#define CD186x_SCHR1 0x09 /* Special Character Register 1 */ +#define CD186x_SCHR2 0x0a /* Special Character Register 2 */ +#define CD186x_SCHR3 0x0b /* Special Character Register 3 */ +#define CD186x_SCHR4 0x0c /* Special Character Register 4 */ +#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */ +#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */ +#define CD186x_MCR 0x12 /* Modem Change Register */ +#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */ +#define CD186x_MSVR 0x28 /* Modem Signal Value Register */ +#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */ +#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */ +#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */ +#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ +#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ +#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ + + +/* Global Interrupt Vector Register (R/W) */ + +#define GIVR_ITMASK 0x07 /* Interrupt type mask */ +#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ +#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ +#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ +#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ + + +/* Global Interrupt Channel Register (R/W) */ + +#define GICR_CHAN 0x1c /* Channel Number Mask */ +#define GICR_CHAN_OFF 2 /* Channel Number shift */ + + +/* Channel Address Register (R/W) */ + +#define CAR_CHAN 0x07 /* Channel Number Mask */ +#define CAR_A7 0x08 /* A7 Address Extension (unused) */ + + +/* Receive Character Status Register (R/O) */ + +#define RCSR_TOUT 0x80 /* Rx Timeout */ +#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ +#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ +#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ +#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ +#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ +#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ +#define RCSR_BREAK 0x08 /* Break has been detected */ +#define RCSR_PE 0x04 /* Parity Error */ +#define RCSR_FE 0x02 /* Frame Error */ +#define RCSR_OE 0x01 /* Overrun Error */ + + +/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ + +#define CCR_HARDRESET 0x81 /* Reset the chip */ + +#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ + +#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ +#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ +#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ + +#define CCR_SSCH1 0x21 /* Send Special Character 1 */ + +#define CCR_SSCH2 0x22 /* Send Special Character 2 */ + +#define CCR_SSCH3 0x23 /* Send Special Character 3 */ + +#define CCR_SSCH4 0x24 /* Send Special Character 4 */ + +#define CCR_TXEN 0x18 /* Enable Transmitter */ +#define CCR_RXEN 0x12 /* Enable Receiver */ + +#define CCR_TXDIS 0x14 /* Disable Transmitter */ +#define CCR_RXDIS 0x11 /* Disable Receiver */ + + +/* Interrupt Enable Register (R/W) */ + +#define IER_DSR 0x80 /* Enable interrupt on DSR change */ +#define IER_CD 0x40 /* Enable interrupt on CD change */ +#define IER_CTS 0x20 /* Enable interrupt on CTS change */ +#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ +#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ +#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ +#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ +#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ + + +/* Channel Option Register 1 (R/W) */ + +#define COR1_ODDP 0x80 /* Odd Parity */ +#define COR1_PARMODE 0x60 /* Parity Mode mask */ +#define COR1_NOPAR 0x00 /* No Parity */ +#define COR1_FORCEPAR 0x20 /* Force Parity */ +#define COR1_NORMPAR 0x40 /* Normal Parity */ +#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ +#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ +#define COR1_1SB 0x00 /* 1 Stop Bit */ +#define COR1_15SB 0x04 /* 1.5 Stop Bits */ +#define COR1_2SB 0x08 /* 2 Stop Bits */ +#define COR1_CHARLEN 0x03 /* Character Length */ +#define COR1_5BITS 0x00 /* 5 bits */ +#define COR1_6BITS 0x01 /* 6 bits */ +#define COR1_7BITS 0x02 /* 7 bits */ +#define COR1_8BITS 0x03 /* 8 bits */ + + +/* Channel Option Register 2 (R/W) */ + +#define COR2_IXM 0x80 /* Implied XON mode */ +#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ +#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ +#define COR2_LLM 0x10 /* Local Loopback Mode */ +#define COR2_RLM 0x08 /* Remote Loopback Mode */ +#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ +#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ +#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ + + +/* Channel Option Register 3 (R/W) */ + +#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ +#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ +#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ +#define COR3_SCDE 0x10 /* Special Character Detection Enable */ +#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ + + +/* Channel Control Status Register (R/O) */ + +#define CCSR_RXEN 0x80 /* Receiver Enabled */ +#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ +#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ +#define CCSR_TXEN 0x08 /* Transmitter Enabled */ +#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ +#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ + + +/* Modem Change Option Register 1 (R/W) */ + +#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ +#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ +#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ +#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ +#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ + + +/* Modem Change Option Register 2 (R/W) */ + +#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ +#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ +#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ + +/* Modem Change Register (R/W) */ + +#define MCR_DSRCHG 0x80 /* DSR Changed */ +#define MCR_CDCHG 0x40 /* CD Changed */ +#define MCR_CTSCHG 0x20 /* CTS Changed */ + + +/* Modem Signal Value Register (R/W) */ + +#define MSVR_DSR 0x80 /* Current state of DSR input */ +#define MSVR_CD 0x40 /* Current state of CD input */ +#define MSVR_CTS 0x20 /* Current state of CTS input */ +#define MSVR_DTR 0x02 /* Current state of DTR output */ +#define MSVR_RTS 0x01 /* Current state of RTS output */ + + +/* Escape characters */ + +#define CD186x_C_ESC 0x00 /* Escape character */ +#define CD186x_C_SBRK 0x81 /* Start sending BREAK */ +#define CD186x_C_DELAY 0x82 /* Delay output */ +#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */ + +#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */ +#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */ +#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */ + + + +#define SRCR_PKGTYPE 0x80 +#define SRCR_REGACKEN 0x40 +#define SRCR_DAISYEN 0x20 +#define SRCR_GLOBPRI 0x10 +#define SRCR_UNFAIR 0x08 +#define SRCR_AUTOPRI 0x02 +#define SRCR_PRISEL 0x01 + + diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c new file mode 100644 index 000000000..edd80e3d2 --- /dev/null +++ b/drivers/char/specialix.c @@ -0,0 +1,2333 @@ +/* + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. But please read the documentation (specialix.txt) + * first. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * Revision history: + * + * Revision 1.0: April 1st 1997. + * Initial release for alpha testing. + * Revision 1.1: April 14th 1997. + * Incorporated Richard Hudsons suggestions, + * removed some debugging printk's. + * Revision 1.2: April 15th 1997. + * Ported to 2.1.x kernels. + * Revision 1.3: April 17th 1997 + * Backported to 2.0. (Compatibility macros). + * Revision 1.4: April 18th 1997 + * Fixed DTR/RTS bug that caused the card to indicate + * "don't send data" to a modem after the password prompt. + * Fixed bug for premature (fake) interrupts. + * Revision 1.5: April 19th 1997 + * fixed a minor typo in the header file, cleanup a little. + * performance warnings are now MAXed at once per minute. + * Revision 1.6: May 23 1997 + * Changed the specialix=... format to include interrupt. + * Revision 1.7: May 27 1997 + * Made many more debug printk's a compile time option. + * Revision 1.8: Jul 1 1997 + * port to linux-2.1.43 kernel. + * + */ + +#define VERSION "1.8" + + +/* + * There is a bunch of documentation about the card, jumpers, config + * settings, restrictions, cables, device names and numbers in + * ../../Documentation/specialix.txt + */ + +#include <linux/config.h> +#include <linux/module.h> + +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/serial.h> +#include <linux/fcntl.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/tqueue.h> +#include <linux/version.h> + + +/* ************************************************************** */ +/* * This section can be removed when 2.0 becomes outdated.... * */ +/* ************************************************************** */ + +#if LINUX_VERSION_CODE < 131328 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 131371 /* less than 2.1.43 */ +/* This has not been extensively tested yet. Sorry. */ +#warning "You're on your own between 2.1.0 and 2.1.43.... " +#warning "Please use a recent kernel." +#endif +#endif + + +#ifdef TWO_ZERO +#define Get_user(a,b) a = get_user(b) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define queue_task queue_task_irq_off +#else +#define Get_user(a,b) get_user(a,b) +#endif + +/* ************************************************************** */ +/* * End of compatibility section.. * */ +/* ************************************************************** */ + + +#ifndef TWO_ZERO +#include <asm/uaccess.h> +#endif + +#include "specialix_io8.h" +#include "cd1865.h" + + + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SPECIALIX_PARANOIA_CHECK + +/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) + When the IRQ routine leaves the chip in a state that is keeps on + requiring attention, the timer doesn't help either. */ +#undef SPECIALIX_TIMER + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SX_REPORT_FIFO +#undef SX_REPORT_OVERRUN + + + +#ifdef CONFIG_SPECIALIX_RTSCTS +#define SX_CRTSCTS(bla) 1 +#else +#define SX_CRTSCTS(tty) C_CRTSCTS(tty) +#endif + + +/* Used to be outb (0xff, 0x80); */ +#define short_pause() udelay (1) + + +#define SPECIALIX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +DECLARE_TASK_QUEUE(tq_specialix); + + + +#define SPECIALIX_TYPE_NORMAL 1 +#define SPECIALIX_TYPE_CALLOUT 2 + +static struct specialix_board * IRQ_to_board[16] = { NULL, } ; +static struct tty_driver specialix_driver, specialix_callout_driver; +static int specialix_refcount = 0; +static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, }; +static unsigned char * tmp_buf = NULL; +static struct semaphore tmp_buf_sem = MUTEX; + +static unsigned long baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0, +}; + +static struct specialix_board sx_board[SX_NBOARD] = { + { 0, SX_IOBASE1, 9, }, + { 0, SX_IOBASE2, 11, }, + { 0, SX_IOBASE3, 12, }, + { 0, SX_IOBASE4, 15, }, +}; + +static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] = { + { 0, }, +}; + + +#ifdef SPECIALIX_TIMER +static struct timer_list missed_irq_timer; +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#endif + + + +static inline int sx_paranoia_check(struct specialix_port const * port, + kdev_t device, const char *routine) +{ +#ifdef SPECIALIX_PARANOIA_CHECK + static const char *badmagic = + KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; + + if (!port) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SPECIALIX_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for specialix IO8+ driver. + * + */ + +/* Get board number from pointer */ +extern inline int board_No (struct specialix_board * bp) +{ + return bp - sx_board; +} + + +/* Get port number from pointer */ +extern inline int port_No (struct specialix_port const * port) +{ + return SX_PORT(port - sx_port); +} + + +/* Get pointer to board from pointer to port */ +extern inline struct specialix_board * port_Board(struct specialix_port const * port) +{ + return &sx_board[SX_BOARD(port - sx_port)]; +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out_off(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR_off(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in_off(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* + * specialix IO8+ IO range functions. + */ + +extern inline int sx_check_io_range(struct specialix_board * bp) +{ + return check_region (bp->base, SX_IO_SPACE); +} + + +extern inline void sx_request_io_range(struct specialix_board * bp) +{ + request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); +} + + +extern inline void sx_release_io_range(struct specialix_board * bp) +{ + release_region(bp->base, SX_IO_SPACE); +} + + +/* Must be called with enabled interrupts */ +extern inline void sx_long_delay(unsigned long delay) +{ + unsigned long i; + + for (i = jiffies + delay; i > jiffies; ) ; +} + + + +/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ +int sx_set_irq ( struct specialix_board *bp) +{ + int virq; + int i; + + switch (bp->irq) { + /* In the same order as in the docs... */ + case 15: virq = 0;break; + case 12: virq = 1;break; + case 11: virq = 2;break; + case 9: virq = 3;break; + default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; + } + + for (i=0;i<2;i++) { + sx_out(bp, CD186x_CAR, i); + sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); + } + return 1; +} + + +/* Reset and setup CD186x chip */ +static int sx_init_CD186x(struct specialix_board * bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + + save_flags(flags); cli(); + + sx_wait_CCR_off(bp); /* Wait for CCR ready */ + sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + sti(); + sx_long_delay(HZ/20); /* Delay 0.05 sec */ + cli(); + sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ + sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ + sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ + sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ + sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ + /* Set RegAckEn */ + sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + scaler = SX_OSCFREQ/SPECIALIX_TPS; + + sx_out_off(bp, CD186x_PPRH, scaler >> 8); + sx_out_off(bp, CD186x_PPRL, scaler & 0xff); + + if (!sx_set_irq (bp)) { + /* Figure out how to pass this along... */ + printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); + rv = 0; + } + + restore_flags(flags); + return rv; +} + + +int read_cross_byte (struct specialix_board *bp, int reg, int bit) +{ + int i; + int t; + + for (i=0, t=0;i<8;i++) { + sx_out_off (bp, CD186x_CAR, i); + if (sx_in_off (bp, reg) & bit) + t |= 1 << i; + } + return t; +} + + +#ifdef SPECIALIX_TIMER +void missed_irq (unsigned long data) +{ + if (sx_in ((struct specialix_board *)data, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)) { + printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); + sx_interrupt (((struct specialix_board *)data)->irq, + NULL, NULL); + } + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +} +#endif + + + +/* Main probing routine, also sets irq. */ +static int sx_probe(struct specialix_board *bp) +{ + unsigned char val1, val2; +#if 0 + int irqs = 0; + int retries; +#endif + int rev; + int chip; + + if (sx_check_io_range(bp)) + return 1; + + /* Are the I/O ports here ? */ + sx_out_off(bp, CD186x_PPRL, 0x5a); + short_pause (); + val1 = sx_in_off(bp, CD186x_PPRL); + + sx_out_off(bp, CD186x_PPRL, 0xa5); + short_pause (); + val2 = sx_in_off(bp, CD186x_PPRL); + + + if ((val1 != 0x5a) || (val2 != 0xa5)) { + printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + /* Check the DSR lines that Specialix uses as board + identification */ + val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); +#endif + if (val1 != 0xb2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + +#if 0 + /* It's time to find IRQ for this board */ + for (retries = 0; retries < 5 && irqs <= 0; retries++) { + irqs = probe_irq_on(); + sx_init_CD186x(bp); /* Reset CD186x chip */ + sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ + sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ + sx_long_delay(HZ/20); + irqs = probe_irq_off(irqs); + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); + printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); + printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); + printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR)); + printk ( "\n"); +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + /* Hmmm. This is dead code anyway. */ + } +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", + val1, val2, val3); +#endif + + } + +#if 0 + if (irqs <= 0) { + printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", + board_No(bp), bp->base); + return 1; + } +#endif + printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); + if (irqs > 0) + bp->irq = irqs; +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + return -EIO; + } + + sx_request_io_range(bp); + bp->flags |= SX_BOARD_PRESENT; + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (sx_in_off(bp, CD186x_GFRCR)) { + case 0x82:chip = 1864;rev='A';break; + case 0x83:chip = 1865;rev='A';break; + case 0x84:chip = 1865;rev='B';break; + case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ + default:chip=-1;rev='x'; + } + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) ); +#endif + +#ifdef SPECIALIX_TIMER + init_timer (&missed_irq_timer); + missed_irq_timer.function = missed_irq; + missed_irq_timer.data = (unsigned long) bp; + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +#endif + + printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), + bp->base, bp->irq, + chip, rev); + + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +extern inline void sx_mark_event(struct specialix_port * port, int event) +{ + /* + * I'm not quite happy with current scheme all serial + * drivers use their own BH routine. + * It seems this easily can be done with one BH routine + * serving for all serial drivers. + * For now I must introduce another one - SPECIALIX_BH. + * Still hope this will be changed in near future. + * -- Dmitry. + */ + set_bit(event, &port->event); + queue_task(&port->tqueue, &tq_specialix); + mark_bh(SPECIALIX_BH); +} + + +extern inline struct specialix_port * sx_get_port(struct specialix_board * bp, + unsigned char const * what) +{ + unsigned char channel; + struct specialix_port * port; + + channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + if (channel < CD186x_NCH) { + port = &sx_port[board_No(bp) * SX_NPORT + channel]; + if (port->flags & ASYNC_INITIALIZED) { + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +extern inline void sx_receive_exc(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + return; + } + +#ifdef SX_REPORT_OVERRUN + status = sx_in(bp, CD186x_RCSR); + if (status & RCSR_OE) { + port->overrun++; +#if SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); +#endif + } + status &= port->mark_mask; +#else + status = sx_in(bp, CD186x_RCSR) & port->mark_mask; +#endif + ch = sx_in(bp, CD186x_RDR); + if (!status) { + return; + } + if (status & RCSR_TOUT) { + printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); + return; + + } else if (status & RCSR_BREAK) { +#ifdef SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); +#endif + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (port->flags & ASYNC_SAK) + do_SAK(tty); + + } else if (status & RCSR_PE) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + + else if (status & RCSR_FE) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + + else if (status & RCSR_OE) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + + else + *tty->flip.flag_buf_ptr++ = 0; + + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_receive(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + + count = sx_in(bp, CD186x_RDCR); + +#ifdef SX_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + break; + } + *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); + *tty->flip.flag_buf_ptr++ = 0; + tty->flip.count++; + } + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_transmit(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + + if (!(port = sx_get_port(bp, "Transmit"))) + return; + + tty = port->tty; + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || tty->stopped || tty->hw_stopped) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = MIN(port->break_length, 0xff); + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_DELAY); + sx_out(bp, CD186x_TDR, count); + if (!(port->break_length -= count)) + port->break_length--; + } else { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_EBRK); + sx_out(bp, CD186x_COR2, port->COR2); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + return; + } + + count = CD186x_NFIFO; + do { + sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); +} + + +extern inline void sx_check_modem(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char mcr; + +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Modem intr. "); +#endif + if (!(port = sx_get_port(bp, "Modem"))) + return; + + tty = port->tty; + + mcr = sx_in(bp, CD186x_MCR); + printk ("mcr = %02x.\n", mcr); + + if ((mcr & MCR_CDCHG)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "CD just changed... "); +#endif + if (sx_in(bp, CD186x_MSVR) & MSVR_CD) { +#ifdef SPECIALIX_DEBUG + printk ( "Waking up guys in open.\n"); +#endif + wake_up_interruptible(&port->open_wait); + } + else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SPECIALIX_DEBUG + printk ( "Sending HUP.\n"); +#endif + queue_task(&port->tqueue_hangup, + &tq_scheduler); + } else { +#ifdef SPECIALIX_DEBUG + printk ( "Don't need to send HUP.\n"); +#endif + } + } + +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } +#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + sx_out(bp, CD186x_MCR, 0); +} + + +/* The main interrupt processing routine */ +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + unsigned char status; + unsigned char ack; + struct specialix_board *bp; + unsigned long loop = 0; + int saved_reg; + + bp = IRQ_to_board[irq]; + + if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq); +#endif + return; + } + + saved_reg = bp->reg; + + while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)))) { + if (status & SRSR_RREQint) { + ack = sx_in(bp, CD186x_RRAR); + + if (ack == (SX_ID | GIVR_IT_RCV)) + sx_receive(bp); + else if (ack == (SX_ID | GIVR_IT_REXC)) + sx_receive_exc(bp); + else + printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n", + board_No(bp), ack); + + } else if (status & SRSR_TREQint) { + ack = sx_in(bp, CD186x_TRAR); + + if (ack == (SX_ID | GIVR_IT_TX)) + sx_transmit(bp); + else + printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n", + board_No(bp), ack); + } else if (status & SRSR_MREQint) { + ack = sx_in(bp, CD186x_MRAR); + + if (ack == (SX_ID | GIVR_IT_MODEM)) + sx_check_modem(bp); + else + printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n", + board_No(bp), ack); + + } + + sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ + } + bp->reg = saved_reg; + outb (bp->reg, bp->base + SX_ADDR_REG); +} + + +/* + * Routines for open & close processing. + */ + + +/* Called with disabled interrupts */ +extern inline int sx_setup_board(struct specialix_board * bp) +{ + int error; + + if (bp->flags & SX_BOARD_ACTIVE) + return 0; + + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + + if (error) + return error; + + IRQ_to_board[bp->irq] = bp; + (void) sx_in (bp, 0); /* Turn ON interrupts. */ + + bp->flags |= SX_BOARD_ACTIVE; + + MOD_INC_USE_COUNT; + return 0; +} + + +/* Called with disabled interrupts */ +extern inline void sx_shutdown_board(struct specialix_board *bp) +{ + if (!(bp->flags & SX_BOARD_ACTIVE)) + return; + + bp->flags &= ~SX_BOARD_ACTIVE; + + free_irq(bp->irq, NULL); + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ + + IRQ_to_board[bp->irq] = NULL; + + MOD_DEC_USE_COUNT; +} + + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static int again=0; + + if (!(tty = port->tty) || !tty->termios) + return; + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + sx_out(bp, CD186x_CAR, port_No(port)); + + /* The Specialix board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + if (SX_CRTSCTS(tty)) + port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + else + port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR); +#endif + baud = C_BAUD(tty); + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 2) + port->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud ++; + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud += 2; + } + + + if (!baud_table[baud]) { + /* Drop DTR & exit */ +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Dropping DTR... Hmm....\n"); +#endif + if (!SX_CRTSCTS (tty)) { + port -> MSVR &= ~ MSVR_DTR; + sx_out(bp, CD186x_MSVR, port->MSVR ); + } +#ifdef DEBUG_SPECIALIX + else + printk (KERN_DEBUG "Can't drop DTR: no DTR.\n"); +#endif + return; + } else { + /* Set DTR on */ + if (!SX_CRTSCTS (tty)) { + port ->MSVR |= MSVR_DTR; + } + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + CD186x_TPC/2) / CD186x_TPC); + if ((tmp < 0x10) && (again < jiffies)) { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n", + port_No (port), tmp); + } else { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. " + "This might not work.\n", + port_No (port), tmp); + } + } + + sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_RBPRL, tmp & 0xff); + sx_out(bp, CD186x_TBPRL, tmp & 0xff); + + baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + sx_out(bp, CD186x_RTPR, tmp); + + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= SPECIALIX_RXFIFO; + /* Setting up CD186x channel registers */ + sx_out(bp, CD186x_COR1, cor1); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_COR3, cor3); + /* Make CD186x know about registers change */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ +#ifdef DEBUG_SPECIALIX + printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); +#endif + sx_out(bp, CD186x_MCOR1, mcor1); + sx_out(bp, CD186x_MCOR2, mcor2); + /* Enable CD186x transmitter & receiver */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + sx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + sx_out(bp, CD186x_MSVR, port->MSVR); +} + + +/* Must be called with interrupts enabled */ +static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port) +{ + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + if (!(tmp = get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (port->xmit_buf) { + free_page(tmp); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + save_flags(flags); cli(); + + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + + if (port->count == 1) + bp->count++; + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + sx_change_speed(bp, port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SX_REPORT_OVERRUN + printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n", + board_No(bp), port_No(port), port->overrun); +#endif +#ifdef SX_REPORT_FIFO + { + int i; + + printk(KERN_INFO "sx%d: port %d: FIFO hits [ ", + board_No(bp), port_No(port)); + for (i = 0; i < 10; i++) { + printk("%ld ", port->hits[i]); + } + printk("].\n"); + } +#endif + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + sx_out(bp, CD186x_CAR, port_No(port)); + + if (!(tty = port->tty) || C_HUPCL(tty)) { + /* Drop DTR */ + sx_out(bp, CD186x_MSVDTR, 0); + } + + /* Reset port */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + sx_out(bp, CD186x_IER, port->IER); + + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->flags &= ~ASYNC_INITIALIZED; + + if (--bp->count < 0) { + printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n", + board_No(bp), bp->count); + bp->count = 0; + } + + /* + * If this is the last opened port on the board + * shutdown whole board + */ + if (!bp->count) + sx_shutdown_board(bp); +} + + +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct specialix_port *port) +{ + struct wait_queue wait = { current, NULL }; + struct specialix_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) { + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; + while (1) { + cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) { + if (SX_CRTSCTS (tty)) { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } else { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } + } + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + + +static int sx_open(struct tty_struct * tty, struct file * filp) +{ + int board; + int error; + struct specialix_port * port; + struct specialix_board * bp; + unsigned long flags; + + board = SX_BOARD(MINOR(tty->device)); + + if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) + return -ENODEV; + + bp = &sx_board[board]; + port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device)); + +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(MINOR(tty->device))); +#endif + + if (sx_paranoia_check(port, tty->device, "sx_open")) + return -ENODEV; + + if ((error = sx_setup_board(bp))) + return error; + + port->count++; + tty->driver_data = port; + port->tty = tty; + + if ((error = sx_setup_port(bp, port))) + return error; + + if ((error = block_til_ready(tty, filp, port))) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; + return 0; +} + + +static void sx_close(struct tty_struct * tty, struct file * filp) +{ + struct specialix_port *port = (struct specialix_port *) tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + unsigned long timeout; + + if (!port || sx_paranoia_check(port, tty->device, "close")) + return; + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + bp = port_Board(port); + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_ERR "sx%d: sx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + port->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while(port->IER & IER_TXEMPTY) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->timeout; + schedule(); + if (jiffies > timeout) { + printk (KERN_INFO "Timeout waiting for close\n"); + break; + } + } + + } + sx_shutdown_port(bp, port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->close_delay; + schedule(); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +} + + +static int sx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + int c, total = 0; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_write")) + return 0; + + bp = port_Board(port); + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + + if (from_user) + down(&tmp_buf_sem); + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + } else + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&tmp_buf_sem); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); + return total; +} + + +static void sx_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; + + save_flags(flags); cli(); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + restore_flags(flags); +} + + +static void sx_flush_chars(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + save_flags(flags); cli(); + port->IER |= IER_TXRDY; + sx_out(port_Board(port), CD186x_CAR, port_No(port)); + sx_out(port_Board(port), CD186x_IER, port->IER); + restore_flags(flags); +} + + +static int sx_write_room(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int ret; + + if (sx_paranoia_check(port, tty->device, "sx_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + + +static int sx_chars_in_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + + if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + + +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int sx_get_modem_info(struct specialix_port * port, unsigned int *value) +{ + struct specialix_board * bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + bp = port_Board(port); + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + status = sx_in(bp, CD186x_MSVR); + restore_flags(flags); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in (bp, CD186x_CAR)); + printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port); +#endif + if (SX_CRTSCTS(port->tty)) { + result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + put_user(result,(unsigned long *) value); + return 0; +} + + +static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + struct specialix_board *bp = port_Board(port); + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + + Get_user(arg, (unsigned long *) value); + switch (cmd) { + case TIOCMBIS: + /* if (arg & TIOCM_RTS) + port->MSVR |= MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; */ + + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + break; + case TIOCMBIC: + /* if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; */ + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + break; + case TIOCMSET: + /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : + (port->MSVR & ~MSVR_RTS); */ + /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); */ + if (SX_CRTSCTS(port->tty)) { + port->MSVR = (arg & TIOCM_RTS) ? + (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); + } else { + port->MSVR = (arg & TIOCM_DTR) ? + (port->MSVR | MSVR_DTR): + (port->MSVR & ~MSVR_DTR); + } + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); + return 0; +} + + +extern inline void sx_send_break(struct specialix_port * port, unsigned long length) +{ + struct specialix_board *bp = port_Board(port); + unsigned long flags; + + save_flags(flags); cli(); + port->break_length = SPECIALIX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_IER, port->IER); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + sx_wait_CCR(bp); + restore_flags(flags); +} + + +extern inline int sx_set_serial_info(struct specialix_port * port, + struct serial_struct * newinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int change_speed; + unsigned long flags; + int error; + + error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp)); + if (error) + return error; + + copy_from_user(&tmp, newinfo, sizeof(tmp)); + +#if 0 + if ((tmp.irq != bp->irq) || + (tmp.port != bp->base) || + (tmp.type != PORT_CIRRUS) || + (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || + (tmp.custom_divisor != 0) || + (tmp.xmit_fifo_size != CD186x_NFIFO) || + (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) + return -EINVAL; +#endif + + change_speed = ((port->flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + + if (!suser()) { + if ((tmp.close_delay != port->close_delay) || + (tmp.closing_wait != port->closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + } else { + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->close_delay = tmp.close_delay; + port->closing_wait = tmp.closing_wait; + } + if (change_speed) { + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + return 0; +} + + +extern inline int sx_get_serial_info(struct specialix_port * port, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int error; + + error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)); + if (error) + return error; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port - sx_port; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->flags; + tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->close_delay * HZ/100; + tmp.closing_wait = port->closing_wait * HZ/100; + tmp.xmit_fifo_size = CD186x_NFIFO; + copy_to_user(retinfo, &tmp, sizeof(tmp)); + return 0; +} + + +static int sx_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int error; + int retval; + + if (sx_paranoia_check(port, tty->device, "sx_ioctl")) + return -ENODEV; + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + sx_send_break(port, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + Get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return sx_get_modem_info(port, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return sx_set_modem_info(port, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return sx_get_serial_info(port, (struct serial_struct *) arg); + case TIOCSSERIAL: + return sx_set_serial_info(port, (struct serial_struct *) arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void sx_throttle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_throttle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + + /* Use DTR instead of RTS ! */ + if (SX_CRTSCTS (tty)) + port->MSVR &= ~MSVR_DTR; + else { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No (port)); + } + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH2); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_unthrottle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_unthrottle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + /* XXXX Use DTR INSTEAD???? */ + if (SX_CRTSCTS(tty)) { + port->MSVR |= MSVR_DTR; + } /* Else clause: see remark in "sx_throttle"... */ + + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH1); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_stop(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_stop")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + restore_flags(flags); +} + + +static void sx_start(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_start")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_sx_hangup() -> tty->hangup() -> sx_hangup() + * + */ +static void do_sx_hangup(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + tty = port->tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static void sx_hangup(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + + if (sx_paranoia_check(port, tty->device, "sx_hangup")) + return; + + bp = port_Board(port); + + sx_shutdown_port(bp, port); + port->event = 0; + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + + +static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + sx_change_speed(port_Board(port), port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + sx_start(tty); + } +} + + +static void do_specialix_bh(void) +{ + run_task_queue(&tq_specialix); +} + + +static void do_softint(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + if(!(tty = port->tty)) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + + +static int sx_init_drivers(void) +{ + int error; + int i; + + + if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) { + printk(KERN_ERR "sx: Couldn't get free page.\n"); + return 1; + } + init_bh(SPECIALIX_BH, do_specialix_bh); + memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); + memset(&specialix_driver, 0, sizeof(specialix_driver)); + specialix_driver.magic = TTY_DRIVER_MAGIC; + specialix_driver.name = "ttyW"; + specialix_driver.major = SPECIALIX_NORMAL_MAJOR; + specialix_driver.num = SX_NBOARD * SX_NPORT; + specialix_driver.type = TTY_DRIVER_TYPE_SERIAL; + specialix_driver.subtype = SPECIALIX_TYPE_NORMAL; + specialix_driver.init_termios = tty_std_termios; + specialix_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + specialix_driver.flags = TTY_DRIVER_REAL_RAW; + specialix_driver.refcount = &specialix_refcount; + specialix_driver.table = specialix_table; + specialix_driver.termios = specialix_termios; + specialix_driver.termios_locked = specialix_termios_locked; + + specialix_driver.open = sx_open; + specialix_driver.close = sx_close; + specialix_driver.write = sx_write; + specialix_driver.put_char = sx_put_char; + specialix_driver.flush_chars = sx_flush_chars; + specialix_driver.write_room = sx_write_room; + specialix_driver.chars_in_buffer = sx_chars_in_buffer; + specialix_driver.flush_buffer = sx_flush_buffer; + specialix_driver.ioctl = sx_ioctl; + specialix_driver.throttle = sx_throttle; + specialix_driver.unthrottle = sx_unthrottle; + specialix_driver.set_termios = sx_set_termios; + specialix_driver.stop = sx_stop; + specialix_driver.start = sx_start; + specialix_driver.hangup = sx_hangup; + + specialix_callout_driver = specialix_driver; + specialix_callout_driver.name = "cuw"; + specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR; + specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT; + + if ((error = tty_register_driver(&specialix_driver))) { + free_page((unsigned long)tmp_buf); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&specialix_callout_driver))) { + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n", + error); + return 1; + } + + memset(sx_port, 0, sizeof(sx_port)); + for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { + sx_port[i].callout_termios = specialix_callout_driver.init_termios; + sx_port[i].normal_termios = specialix_driver.init_termios; + sx_port[i].magic = SPECIALIX_MAGIC; + sx_port[i].tqueue.routine = do_softint; + sx_port[i].tqueue.data = &sx_port[i]; + sx_port[i].tqueue_hangup.routine = do_sx_hangup; + sx_port[i].tqueue_hangup.data = &sx_port[i]; + sx_port[i].close_delay = 50 * HZ/100; + sx_port[i].closing_wait = 3000 * HZ/100; + } + + return 0; +} + + +static void sx_release_drivers(void) +{ + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + tty_unregister_driver(&specialix_callout_driver); +} + + +#ifndef MODULE +/* + * Called at boot time. + * + * You can specify IO base for up to SX_NBOARD cards, + * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt. + * Note that there will be no probing at default + * addresses in this case. + * + */ +void specialix_setup(char *str, int * ints) +{ + int i; + + for (i=0;i<SX_NBOARD;i++) { + sx_board[i].base = 0; + } + + for (i = 1; i <= ints[0]; i++) { + if (i&1) + sx_board[i/2].base = ints[i]; + else + sx_board[i/2 -1].irq = ints[i]; + } +} +#endif + +/* + * This routine must be called by kernel at boot time + */ +int specialix_init(void) +{ + int i; + int found = 0; + + printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n"); + printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n"); +#ifdef CONFIG_SPECIALIX_RTSCTS + printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); +#else + printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n"); +#endif + + if (sx_init_drivers()) + return -EIO; + + for (i = 0; i < SX_NBOARD; i++) + if (sx_board[i].base && !sx_probe(&sx_board[i])) + found++; + + if (!found) { + sx_release_drivers(); + printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n"); + return -EIO; + } + + return 0; +} + +#ifdef MODULE +int iobase[SX_NBOARD] = {0,}; + +int irq [SX_NBOARD] = {0,}; + +/* + * You can setup up to 4 boards. + * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter. + * + * More than 4 boards in one computer is not possible, as the card can + * only use 4 different interrupts. + * + */ +int init_module(void) +{ + int i; + + if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) { + for(i = 0; i < SX_NBOARD; i++) { + sx_board[i].base = iobase[i]; + sx_board[i].irq = irq[i]; + } + } + + return specialix_init(); +} + + +void cleanup_module(void) +{ + int i; + + sx_release_drivers(); + for (i = 0; i < SX_NBOARD; i++) + if (sx_board[i].flags & SX_BOARD_PRESENT) + sx_release_io_range(&sx_board[i]); +#ifdef SPECIALIX_TIMER + del_timer (&missed_irq_timer); +#endif + +} +#endif /* MODULE */ diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h new file mode 100644 index 000000000..f35c959e4 --- /dev/null +++ b/drivers/char/specialix_io8.h @@ -0,0 +1,146 @@ +/* + * linux/drivers/char/specialix_io8.h -- + * Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * */ + +#ifndef __LINUX_SPECIALIX_H +#define __LINUX_SPECIALIX_H + +#include <linux/serial.h> + +#ifdef __KERNEL__ + +#define SX_NBOARD 4 +/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ +#define SX_IO_SPACE 4 +/* eight ports per board. */ +#define SX_NPORT 8 +#define SX_BOARD(line) ((line) / SX_NPORT) +#define SX_PORT(line) ((line) & (SX_NPORT - 1)) + + +#define SX_DATA_REG 0 /* Base+0 : Data register */ +#define SX_ADDR_REG 1 /* base+1 : Address register. */ + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SX_OSCFREQ (25 MHz/2) +/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ + + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SPECIALIX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SPECIALIX_MAGIC 0x0907 + +#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ + +#define SX_IOBASE1 0x100 +#define SX_IOBASE2 0x180 +#define SX_IOBASE3 0x250 +#define SX_IOBASE4 0x260 + +struct specialix_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + signed char count; + unsigned char DTR; + int reg; +}; + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_BOARD_ACTIVE 0x00000002 + + +struct specialix_port { + int magic; + int baud_base; + int flags; + struct tty_struct * tty; + int count; + int blocked_open; + int event; + int timeout; + int close_delay; + long session; + long pgrp; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + short wakeup_chars; + short break_length; + unsigned short closing_wait; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef SX_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef SX_REPORT_FIFO + unsigned long hits[10]; +#endif +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SPECIALIX_H */ + + + + + + + + + diff --git a/drivers/misc/parport_ax.c b/drivers/misc/parport_ax.c new file mode 100644 index 000000000..caaccd6e7 --- /dev/null +++ b/drivers/misc/parport_ax.c @@ -0,0 +1,554 @@ +/* $Id: parport_ax.c,v 1.2 1997/10/25 17:27:03 philip Exp $ + * Parallel-port routines for Sun Ultra/AX architecture + * + * Author: Eddie C. Dost <ecd@skynet.be> + * + * based on work by: + * Phil Blundell <Philip.Blundell@pobox.com> + * Tim Waugh <tim@cyberelk.demon.co.uk> + * Jose Renau <renau@acm.org> + * David Campbell <campbell@tirian.che.curtin.edu.au> + * Grant Guenther <grant@torque.net> + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/malloc.h> + +#include <linux/parport.h> + +#include <asm/ptrace.h> +#include <linux/interrupt.h> + +#include <asm/io.h> +#include <asm/ebus.h> +#include <asm/ns87303.h> + + +/* + * Define this if you have Devices which don't support short + * host read/write cycles. + */ +#undef HAVE_SLOW_DEVICES + + +#define DATA 0x00 +#define STATUS 0x01 +#define CONTROL 0x02 + +#define CFIFO 0x400 +#define DFIFO 0x400 +#define TFIFO 0x400 +#define CNFA 0x400 +#define CNFB 0x401 +#define ECR 0x402 + +static void +ax_null_intr_func(int irq, void *dev_id, struct pt_regs *regs) +{ + /* NULL function - Does nothing */ + return; +} + +#if 0 +static unsigned int +ax_read_configb(struct parport *p) +{ + return (unsigned int)inb(p->base + CNFB); +} +#endif + +static void +ax_write_data(struct parport *p, unsigned int d) +{ + outb(d, p->base + DATA); +} + +static unsigned int +ax_read_data(struct parport *p) +{ + return (unsigned int)inb(p->base + DATA); +} + +static void +ax_write_control(struct parport *p, unsigned int d) +{ + outb(d, p->base + CONTROL); +} + +static unsigned int +ax_read_control(struct parport *p) +{ + return (unsigned int)inb(p->base + CONTROL); +} + +static unsigned int +ax_frob_control(struct parport *p, unsigned int mask, unsigned int val) +{ + unsigned int old = (unsigned int)inb(p->base + CONTROL); + outb(((old & ~mask) ^ val), p->base + CONTROL); + return old; +} + +static void +ax_write_status(struct parport *p, unsigned int d) +{ + outb(d, p->base + STATUS); +} + +static unsigned int +ax_read_status(struct parport *p) +{ + return (unsigned int)inb(p->base + STATUS); +} + +static void +ax_write_econtrol(struct parport *p, unsigned int d) +{ + outb(d, p->base + ECR); +} + +static unsigned int +ax_read_econtrol(struct parport *p) +{ + return (unsigned int)inb(p->base + ECR); +} + +static unsigned int +ax_frob_econtrol(struct parport *p, unsigned int mask, unsigned int val) +{ + unsigned int old = (unsigned int)inb(p->base + ECR); + outb(((old & ~mask) ^ val), p->base + ECR); + return old; +} + +static void +ax_change_mode(struct parport *p, int m) +{ + ax_frob_econtrol(p, 0xe0, m << 5); +} + +static void +ax_write_fifo(struct parport *p, unsigned int v) +{ + outb(v, p->base + DFIFO); +} + +static unsigned int +ax_read_fifo(struct parport *p) +{ + return inb(p->base + DFIFO); +} + +static void +ax_disable_irq(struct parport *p) +{ + struct linux_ebus_dma *dma = p->private_data; + unsigned int dcsr; + + dcsr = readl((unsigned long)&dma->dcsr); + dcsr &= ~(EBUS_DCSR_INT_EN); + writel(dcsr, (unsigned long)&dma->dcsr); +} + +static void +ax_enable_irq(struct parport *p) +{ + struct linux_ebus_dma *dma = p->private_data; + unsigned int dcsr; + + dcsr = readl((unsigned long)&dma->dcsr); + dcsr |= EBUS_DCSR_INT_EN; + writel(dcsr, (unsigned long)&dma->dcsr); +} + +static void +ax_release_resources(struct parport *p) +{ + if (p->irq != PARPORT_IRQ_NONE) { + ax_disable_irq(p); + free_irq(p->irq, NULL); + } + release_region(p->base, p->size); + if (p->modes & PARPORT_MODE_PCECR) + release_region(p->base+0x400, 3); + release_region((unsigned long)p->private_data, + sizeof(struct linux_ebus_dma)); +} + +static int +ax_claim_resources(struct parport *p) +{ + /* FIXME check that resources are free */ + if (p->irq != PARPORT_IRQ_NONE) { + request_irq(p->irq, ax_null_intr_func, 0, p->name, NULL); + ax_enable_irq(p); + } + request_region(p->base, p->size, p->name); + if (p->modes & PARPORT_MODE_PCECR) + request_region(p->base+0x400, 3, p->name); + request_region((unsigned long)p->private_data, + sizeof(struct linux_ebus_dma), p->name); + return 0; +} + +static void +ax_save_state(struct parport *p, struct parport_state *s) +{ + s->u.pc.ctr = ax_read_control(p); + s->u.pc.ecr = ax_read_econtrol(p); +} + +static void +ax_restore_state(struct parport *p, struct parport_state *s) +{ + ax_write_control(p, s->u.pc.ctr); + ax_write_econtrol(p, s->u.pc.ecr); +} + +static unsigned int +ax_epp_read_block(struct parport *p, void *buf, unsigned int length) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_epp_write_block(struct parport *p, void *buf, unsigned int length) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_ecp_read_block(struct parport *p, void *buf, unsigned int length, + void (*fn)(struct parport *, void *, unsigned int), + void *handle) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_ecp_write_block(struct parport *p, void *buf, unsigned int length, + void (*fn)(struct parport *, void *, unsigned int), + void *handle) +{ + return 0; /* FIXME */ +} + +static int +ax_examine_irq(struct parport *p) +{ + return 0; /* FIXME */ +} + +static void +ax_inc_use_count(void) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void +ax_dec_use_count(void) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static struct parport_operations ax_ops = +{ + ax_write_data, + ax_read_data, + + ax_write_control, + ax_read_control, + ax_frob_control, + + ax_write_econtrol, + ax_read_econtrol, + ax_frob_econtrol, + + ax_write_status, + ax_read_status, + + ax_write_fifo, + ax_read_fifo, + + ax_change_mode, + + ax_release_resources, + ax_claim_resources, + + ax_epp_write_block, + ax_epp_read_block, + + ax_ecp_write_block, + ax_ecp_read_block, + + ax_save_state, + ax_restore_state, + + ax_enable_irq, + ax_disable_irq, + ax_examine_irq, + + ax_inc_use_count, + ax_dec_use_count +}; + + +/****************************************************** + * MODE detection section: + */ + +/* Check for ECP + * + * Old style XT ports alias io ports every 0x400, hence accessing ECR + * on these cards actually accesses the CTR. + * + * Modern cards don't do this but reading from ECR will return 0xff + * regardless of what is written here if the card does NOT support + * ECP. + * + * We will write 0x2c to ECR and 0xcc to CTR since both of these + * values are "safe" on the CTR since bits 6-7 of CTR are unused. + */ +static int parport_ECR_present(struct parport *pb) +{ + unsigned int r, octr = pb->ops->read_control(pb), + oecr = pb->ops->read_econtrol(pb); + + r = pb->ops->read_control(pb); + if ((pb->ops->read_econtrol(pb) & 0x3) == (r & 0x3)) { + pb->ops->write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */ + + r = pb->ops->read_control(pb); + if ((pb->ops->read_econtrol(pb) & 0x2) == (r & 0x2)) { + pb->ops->write_control(pb, octr); + return 0; /* Sure that no ECR register exists */ + } + } + + if ((pb->ops->read_econtrol(pb) & 0x3 ) != 0x1) + return 0; + + pb->ops->write_econtrol(pb, 0x34); + if (pb->ops->read_econtrol(pb) != 0x35) + return 0; + + pb->ops->write_econtrol(pb, oecr); + pb->ops->write_control(pb, octr); + + return PARPORT_MODE_PCECR; +} + +static int parport_ECP_supported(struct parport *pb) +{ + int i, oecr = pb->ops->read_econtrol(pb); + + /* If there is no ECR, we have no hope of supporting ECP. */ + if (!(pb->modes & PARPORT_MODE_PCECR)) + return 0; + + /* + * Using LGS chipset it uses ECR register, but + * it doesn't support ECP or FIFO MODE + */ + + pb->ops->write_econtrol(pb, 0xc0); /* TEST FIFO */ + for (i=0; i < 1024 && (pb->ops->read_econtrol(pb) & 0x01); i++) + pb->ops->write_fifo(pb, 0xaa); + + pb->ops->write_econtrol(pb, oecr); + return (i == 1024) ? 0 : PARPORT_MODE_PCECP; +} + +/* Detect PS/2 support. + * + * Bit 5 (0x20) sets the PS/2 data direction; setting this high + * allows us to read data from the data lines. In theory we would get back + * 0xff but any peripheral attached to the port may drag some or all of the + * lines down to zero. So if we get back anything that isn't the contents + * of the data register we deem PS/2 support to be present. + * + * Some SPP ports have "half PS/2" ability - you can't turn off the line + * drivers, but an external peripheral with sufficiently beefy drivers of + * its own can overpower them and assert its own levels onto the bus, from + * where they can then be read back as normal. Ports with this property + * and the right type of device attached are likely to fail the SPP test, + * (as they will appear to have stuck bits) and so the fact that they might + * be misdetected here is rather academic. + */ + +static int parport_PS2_supported(struct parport *pb) +{ + int ok = 0, octr = pb->ops->read_control(pb); + + pb->ops->write_control(pb, octr | 0x20); /* try to tri-state buffer */ + + pb->ops->write_data(pb, 0x55); + if (pb->ops->read_data(pb) != 0x55) ok++; + + pb->ops->write_data(pb, 0xaa); + if (pb->ops->read_data(pb) != 0xaa) ok++; + + pb->ops->write_control(pb, octr); /* cancel input mode */ + + return ok ? PARPORT_MODE_PCPS2 : 0; +} + +static int parport_ECPPS2_supported(struct parport *pb) +{ + int mode, oecr = pb->ops->read_econtrol(pb); + + if (!(pb->modes & PARPORT_MODE_PCECR)) + return 0; + + pb->ops->write_econtrol(pb, 0x20); + + mode = parport_PS2_supported(pb); + + pb->ops->write_econtrol(pb, oecr); + return mode ? PARPORT_MODE_PCECPPS2 : 0; +} + +#define printmode(x) \ +{ \ + if (p->modes & PARPORT_MODE_PC##x) { \ + printk("%s%s", f ? "," : "", #x); \ + f++; \ + } \ +} + +int +init_one_port(struct linux_ebus_device *dev) +{ + struct parport tmpport, *p; + unsigned long base; + unsigned long config; + unsigned char tmp; + int irq, dma; + + /* Pointer to NS87303 Configuration Registers */ + config = dev->base_address[1]; + + /* Setup temporary access to Device operations */ + tmpport.base = dev->base_address[0]; + tmpport.ops = &ax_ops; + + /* Enable ECP mode, set bit 2 of the CTR first */ + tmpport.ops->write_control(&tmpport, 0x04); + tmp = ns87303_readb(config, PCR); + tmp |= (PCR_EPP_IEEE | PCR_ECP_ENABLE | PCR_ECP_CLK_ENA); + ns87303_writeb(config, PCR, tmp); + + /* LPT CTR bit 5 controls direction of parallel port */ + tmp = ns87303_readb(config, PTR); + tmp |= PTR_LPT_REG_DIR; + ns87303_writeb(config, PTR, tmp); + + /* Configure IRQ to Push Pull, Level Low */ + tmp = ns87303_readb(config, PCR); + tmp &= ~(PCR_IRQ_ODRAIN); + tmp |= PCR_IRQ_POLAR; + ns87303_writeb(config, PCR, tmp); + +#ifndef HAVE_SLOW_DEVICES + /* Enable Zero Wait State for ECP */ + tmp = ns87303_readb(config, FCR); + tmp |= FCR_ZWS_ENA; + ns87303_writeb(config, FCR, tmp); +#endif + + /* + * Now continue initializing the port + */ + base = dev->base_address[0]; + irq = dev->irqs[0]; + dma = PARPORT_DMA_AUTO; + + if (!(p = parport_register_port(base, irq, dma, &ax_ops))) + return 0; + + /* Safe away pointer to our EBus DMA */ + p->private_data = (void *)dev->base_address[2]; + + p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p); + if (!check_region(p->base + 0x400, 3)) { + p->modes |= parport_ECR_present(p); + p->modes |= parport_ECP_supported(p); + p->modes |= parport_ECPPS2_supported(p); + } + p->size = 3; + + if (p->dma == PARPORT_DMA_AUTO) + p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE; + + printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); + if (p->irq != PARPORT_IRQ_NONE) + printk(", irq %x", (unsigned int)p->irq); + if (p->dma != PARPORT_DMA_NONE) + printk(", dma %d", p->dma); + printk(" ["); + { + int f = 0; + printmode(SPP); + printmode(PS2); + printmode(ECP); + printmode(ECPPS2); + } + printk("]\n"); + parport_proc_register(p); + p->flags |= PARPORT_FLAG_COMA; + + ax_write_control(p, 0x0c); + ax_write_data(p, 0); + + if (parport_probe_hook) + (*parport_probe_hook)(p); + + return 1; +} + +int +parport_ax_init(void) +{ + struct linux_ebus *ebus; + struct linux_ebus_device *edev; + int count = 0; + + for_all_ebusdev(edev, ebus) + if (!strcmp(edev->prom_name, "ecpp")) + count += init_one_port(edev); + return count; +} + +#ifdef MODULE + +int +init_module(void) +{ + return (parport_ax_init() ? 0 : 1); +} + +void +cleanup_module(void) +{ + struct parport *p = parport_enumerate(), *tmp; + while (p) { + tmp = p->next; + if (p->modes & PARPORT_MODE_PCSPP) { + if (!(p->flags & PARPORT_FLAG_COMA)) + parport_quiesce(p); + parport_proc_unregister(p); + parport_unregister_port(p); + } + p = tmp; + } +} +#endif diff --git a/drivers/net/baycom.c b/drivers/net/baycom.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/baycom.c +++ /dev/null diff --git a/drivers/net/bpqether.c b/drivers/net/bpqether.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/bpqether.c +++ /dev/null diff --git a/drivers/net/dmascc.c b/drivers/net/dmascc.c new file mode 100644 index 000000000..33b473d32 --- /dev/null +++ b/drivers/net/dmascc.c @@ -0,0 +1,1260 @@ +/* + * $Id: dmascc.c,v 1.2 1997/12/02 16:49:49 oe1kib Exp $ + * + * Driver for high-speed SCC boards (those with DMA support) + * Copyright (C) 1997 Klaus Kudielka + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <linux/module.h> +#include <linux/dmascc.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/in.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/netdevice.h> +#include <linux/sockios.h> +#include <linux/tqueue.h> +#include <linux/version.h> +#include <asm/atomic.h> +#include <asm/bitops.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/segment.h> +#include <asm/uaccess.h> +#include <net/ax25.h> +#include <stdio.h> +#include "z8530.h" + + +/* Number of buffers per channel */ + +#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */ +#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */ +#define BUF_SIZE 2016 + + +/* Cards supported */ + +#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \ + 0, 8, 1843200, 3686400 } +#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \ + 0, 8, 3686400, 7372800 } +#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \ + 0, 4, 6144000, 6144000 } + +#define HARDWARE { HW_PI, HW_PI2, HW_TWIN } + +#define TYPE_PI 0 +#define TYPE_PI2 1 +#define TYPE_TWIN 2 +#define NUM_TYPES 3 + +#define MAX_NUM_DEVS 32 + + +/* SCC chips supported */ + +#define Z8530 0 +#define Z85C30 1 +#define Z85230 2 + +#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" } + + +/* I/O registers */ + +/* 8530 registers relative to card base */ +#define SCCB_CMD 0x00 +#define SCCB_DATA 0x01 +#define SCCA_CMD 0x02 +#define SCCA_DATA 0x03 + +/* 8254 registers relative to card base */ +#define TMR_CNT0 0x00 +#define TMR_CNT1 0x01 +#define TMR_CNT2 0x02 +#define TMR_CTRL 0x03 + +/* Additional PI/PI2 registers relative to card base */ +#define PI_DREQ_MASK 0x04 + +/* Additional PackeTwin registers relative to card base */ +#define TWIN_INT_REG 0x08 +#define TWIN_CLR_TMR1 0x09 +#define TWIN_CLR_TMR2 0x0a +#define TWIN_SPARE_1 0x0b +#define TWIN_DMA_CFG 0x08 +#define TWIN_SERIAL_CFG 0x09 +#define TWIN_DMA_CLR_FF 0x0a +#define TWIN_SPARE_2 0x0b + + +/* PackeTwin I/O register values */ + +/* INT_REG */ +#define TWIN_SCC_MSK 0x01 +#define TWIN_TMR1_MSK 0x02 +#define TWIN_TMR2_MSK 0x04 +#define TWIN_INT_MSK 0x07 + +/* SERIAL_CFG */ +#define TWIN_DTRA_ON 0x01 +#define TWIN_DTRB_ON 0x02 +#define TWIN_EXTCLKA 0x04 +#define TWIN_EXTCLKB 0x08 +#define TWIN_LOOPA_ON 0x10 +#define TWIN_LOOPB_ON 0x20 +#define TWIN_EI 0x80 + +/* DMA_CFG */ +#define TWIN_DMA_HDX_T1 0x08 +#define TWIN_DMA_HDX_R1 0x0a +#define TWIN_DMA_HDX_T3 0x14 +#define TWIN_DMA_HDX_R3 0x16 +#define TWIN_DMA_FDX_T3R1 0x1b +#define TWIN_DMA_FDX_T1R3 0x1d + + +/* Status values */ + +/* tx_state */ +#define TX_IDLE 0 +#define TX_OFF 1 +#define TX_TXDELAY 2 +#define TX_ACTIVE 3 +#define TX_SQDELAY 4 + + +/* Data types */ + +struct scc_hardware { + char *name; + int io_region; + int io_delta; + int io_size; + int num_devs; + int scc_offset; + int tmr_offset; + int tmr_hz; + int pclk_hz; +}; + +struct scc_priv { + char name[10]; + struct enet_statistics stats; + struct scc_info *info; + int channel; + int cmd, data, tmr; + struct scc_param param; + char rx_buf[NUM_RX_BUF][BUF_SIZE]; + int rx_len[NUM_RX_BUF]; + int rx_ptr; + struct tq_struct rx_task; + int rx_head, rx_tail, rx_count; + int rx_over; + char tx_buf[NUM_TX_BUF][BUF_SIZE]; + int tx_len[NUM_TX_BUF]; + int tx_ptr; + int tx_head, tx_tail, tx_count; + int tx_sem, tx_state; + unsigned long tx_start; + int status; +}; + +struct scc_info { + int type; + int chip; + int open; + int scc_base; + int tmr_base; + int twin_serial_cfg; + struct device dev[2]; + struct scc_priv priv[2]; + struct scc_info *next; +}; + + +/* Function declarations */ + +int dmascc_init(void) __init; +static int setup_adapter(int io, int h, int n) __init; + +static inline void write_scc(int ctl, int reg, int val); +static inline int read_scc(int ctl, int reg); +static int scc_open(struct device *dev); +static int scc_close(struct device *dev); +static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int scc_send_packet(struct sk_buff *skb, struct device *dev); +static struct enet_statistics *scc_get_stats(struct device *dev); +static int scc_set_mac_address(struct device *dev, void *sa); +static void scc_isr(int irq, void *dev_id, struct pt_regs * regs); +static inline void z8530_isr(struct scc_info *info); +static void rx_isr(struct device *dev); +static void special_condition(struct device *dev, int rc); +static void rx_bh(void *arg); +static void tx_isr(struct device *dev); +static void es_isr(struct device *dev); +static void tm_isr(struct device *dev); +static inline void delay(struct device *dev, int t); +static inline unsigned char random(void); + + +/* Initialization variables */ + +static int io[MAX_NUM_DEVS] __initdata = { 0, }; +/* Beware! hw[] is also used in cleanup_module(). If __initdata also applies + to modules, we may not declare hw[] as __initdata */ +static struct scc_hardware hw[NUM_TYPES] __initdata = HARDWARE; +static char ax25_broadcast[7] __initdata = + { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1 }; +static char ax25_test[7] __initdata = + { 'L'<<1, 'I'<<1, 'N'<<1, 'U'<<1, 'X'<<1, ' '<<1, '1'<<1 }; + + +/* Global variables */ + +static struct scc_info *first = NULL; +static unsigned long rand; + + + +/* Module functions */ + +#ifdef MODULE + + +MODULE_AUTHOR("Klaus Kudielka <oe1kib@oe1xtu.ampr.org>"); +MODULE_DESCRIPTION("Driver for high-speed SCC boards"); +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i"); + + +int init_module(void) +{ + return dmascc_init(); +} + + +void cleanup_module(void) +{ + int i; + struct scc_info *info; + + while (first) { + info = first; + + /* Unregister devices */ + for (i = 0; i < 2; i++) { + if (info->dev[i].name) + unregister_netdev(&info->dev[i]); + } + + /* Reset board */ + if (info->type == TYPE_TWIN) + outb_p(0, info->dev[0].base_addr + TWIN_SERIAL_CFG); + write_scc(info->priv[0].cmd, R9, FHWRES); + release_region(info->dev[0].base_addr, + hw[info->type].io_size); + + /* Free memory */ + first = info->next; + kfree_s(info, sizeof(struct scc_info)); + } +} + + +#else + + +__initfunc(void dmascc_setup(char *str, int *ints)) +{ + int i; + + for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++) + io[i] = ints[i+1]; +} + + +#endif + + +/* Initialization functions */ + +__initfunc(int dmascc_init(void)) +{ + int h, i, j, n, base[MAX_NUM_DEVS], tcmd, t0, t1, status; + unsigned long time, start[MAX_NUM_DEVS], stop[MAX_NUM_DEVS]; + + /* Initialize random number generator */ + rand = jiffies; + + /* Cards found = 0 */ + n = 0; + + /* Run autodetection for each card type */ + for (h = 0; h < NUM_TYPES; h++) { + + if (io[0]) { + /* User-specified I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) base[i] = 0; + for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) { + j = (io[i] - hw[h].io_region) / hw[h].io_delta; + if (j >= 0 && + j < hw[h].num_devs && + hw[h].io_region + j * hw[h].io_delta == io[i]) + base[j] = io[i]; + } + } else { + /* Default I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) + base[i] = hw[h].io_region + i * hw[h].io_delta; + } + + /* Check valid I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i] && check_region(base[i], hw[h].io_size)) + base[i] = 0; + + /* Start timers */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) { + tcmd = base[i] + hw[h].tmr_offset + TMR_CTRL; + t0 = base[i] + hw[h].tmr_offset + TMR_CNT0; + t1 = base[i] + hw[h].tmr_offset + TMR_CNT1; + /* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */ + outb_p(0x36, tcmd); + outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0); + outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0); + /* Timer 1: LSB+MSB, Mode 0, HZ/10 */ + outb_p(0x70, tcmd); + outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1); + outb_p((TMR_0_HZ/HZ*10) >> 8, t1); + /* Timer 2: LSB+MSB, Mode 0 */ + outb_p(0xb0, tcmd); + } + + /* Initialize start values in case we miss the null count bit */ + time = jiffies; + for (i = 0; i < hw[h].num_devs; i++) start[i] = time; + + /* Timing loop */ + while (jiffies - time < 12) { + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) { + /* Read back Timer 1: Status */ + outb_p(0xE4, base[i] + hw[h].tmr_offset + TMR_CTRL); + status = inb_p(base[i] + hw[h].tmr_offset + TMR_CNT1); + if ((status & 0x3F) != 0x30) base[i] = 0; + if (status & 0x40) start[i] = jiffies; + if (~status & 0x80) stop[i] = jiffies; + } + } + + /* Evaluate measurements */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) { + time = stop[i] - start[i]; + if (time < 9 || time > 11) + /* The time expired doesn't match */ + base[i] = 0; + else { + /* Ok, we have found an adapter */ + if (setup_adapter(base[i], h, n) == 0) + n++; + } + } + + } /* NUM_TYPES */ + + /* If any adapter was successfully initialized, return ok */ + if (n) return 0; + + /* If no adapter found, return error */ + printk("dmascc: no adapters found\n"); + return -EIO; +} + + +__initfunc(int setup_adapter(int io, int h, int n)) +{ + int i, irq, chip; + struct scc_info *info; + struct device *dev; + struct scc_priv *priv; + unsigned long time; + unsigned int irqs; + int tmr = io + hw[h].tmr_offset; + int scc = io + hw[h].scc_offset; + int cmd = scc + SCCA_CMD; + char *chipnames[] = CHIPNAMES; + + /* Reset 8530 */ + write_scc(cmd, R9, FHWRES | MIE | NV); + + /* Determine type of chip */ + write_scc(cmd, R15, 1); + if (!read_scc(cmd, R15)) { + /* WR7' not present. This is an ordinary Z8530 SCC. */ + chip = Z8530; + } else { + /* Put one character in TX FIFO */ + write_scc(cmd, R8, 0); + if (read_scc(cmd, R0) & Tx_BUF_EMP) { + /* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */ + chip = Z85230; + } else { + /* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */ + chip = Z85C30; + } + } + write_scc(cmd, R15, 0); + + /* Start IRQ auto-detection */ + sti(); + irqs = probe_irq_on(); + + /* Enable interrupts */ + switch (h) { + case TYPE_PI: + case TYPE_PI2: + outb_p(0, io + PI_DREQ_MASK); + write_scc(cmd, R15, CTSIE); + write_scc(cmd, R0, RES_EXT_INT); + write_scc(cmd, R1, EXT_INT_ENAB); + break; + case TYPE_TWIN: + outb_p(0, io + TWIN_DMA_CFG); + inb_p(io + TWIN_CLR_TMR1); + inb_p(io + TWIN_CLR_TMR2); + outb_p(TWIN_EI, io + TWIN_SERIAL_CFG); + break; + } + + /* Start timer */ + outb_p(1, tmr + TMR_CNT1); + outb_p(0, tmr + TMR_CNT1); + /* Wait and detect IRQ */ + time = jiffies; while (jiffies - time < 2 + HZ / TMR_0_HZ); + irq = probe_irq_off(irqs); + + /* Clear pending interrupt, disable interrupts */ + switch (h) { + case TYPE_PI: + case TYPE_PI2: + write_scc(cmd, R1, 0); + write_scc(cmd, R15, 0); + write_scc(cmd, R0, RES_EXT_INT); + break; + case TYPE_TWIN: + inb_p(io + TWIN_CLR_TMR1); + outb_p(0, io + TWIN_SERIAL_CFG); + break; + } + + if (irq <= 0) { + printk("dmascc: could not find irq of %s at %#3x (irq=%d)\n", + hw[h].name, io, irq); + return -1; + } + + /* Allocate memory */ + info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA); + if (!info) { + printk("dmascc: could not allocate memory for %s at %#3x\n", + hw[h].name, io); + return -1; + } + + /* Set up data structures */ + memset(info, 0, sizeof(struct scc_info)); + info->type = h; + info->chip = chip; + info->scc_base = io + hw[h].scc_offset; + info->tmr_base = io + hw[h].tmr_offset; + info->twin_serial_cfg = 0; + for (i = 0; i < 2; i++) { + dev = &info->dev[i]; + priv = &info->priv[i]; + sprintf(priv->name, "dmascc%i", 2*n+i); + priv->info = info; + priv->channel = i; + priv->cmd = info->scc_base + (i ? SCCB_CMD : SCCA_CMD); + priv->data = info->scc_base + (i ? SCCB_DATA : SCCA_DATA); + priv->tmr = info->tmr_base + (i ? TMR_CNT2 : TMR_CNT1); + priv->param.pclk_hz = hw[h].pclk_hz; + priv->param.brg_tc = -1; + priv->param.clocks = TCTRxCP | RCRTxCP; + priv->param.txdelay = TMR_0_HZ * 10 / 1000; + priv->param.txtime = HZ * 3; + priv->param.sqdelay = TMR_0_HZ * 1 / 1000; + priv->param.slottime = TMR_0_HZ * 10 / 1000; + priv->param.waittime = TMR_0_HZ * 100 / 1000; + priv->param.persist = 32; + priv->rx_task.routine = rx_bh; + priv->rx_task.data = dev; + dev->priv = priv; + dev->name = priv->name; + dev->base_addr = io; + dev->irq = irq; + dev->open = scc_open; + dev->stop = scc_close; + dev->do_ioctl = scc_ioctl; + dev->hard_start_xmit = scc_send_packet; + dev->get_stats = scc_get_stats; + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; + dev->set_mac_address = scc_set_mac_address; + dev->type = ARPHRD_AX25; + dev->hard_header_len = 73; + dev->mtu = 1500; + dev->addr_len = 7; + dev->tx_queue_len = 64; + memcpy(dev->broadcast, ax25_broadcast, 7); + memcpy(dev->dev_addr, ax25_test, 7); + dev->flags = 0; + dev_init_buffers(dev); + if (register_netdev(dev)) { + printk("dmascc: could not register %s\n", dev->name); + dev->name = NULL; + } + } + + request_region(io, hw[h].io_size, "dmascc"); + + info->next = first; + first = info; + printk("dmascc: found %s (%s) at %#3x, irq %d\n", hw[h].name, + chipnames[chip], io, irq); + return 0; +} + + +/* Driver functions */ + +static inline void write_scc(int ctl, int reg, int val) +{ + outb_p(reg, ctl); + outb_p(val, ctl); +} + + +static inline int read_scc(int ctl, int reg) +{ + outb_p(reg, ctl); + return inb_p(ctl); +} + + +static int scc_open(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int io = dev->base_addr; + int cmd = priv->cmd; + + /* Request IRQ if not already used by other channel */ + if (!info->open) { + if (request_irq(dev->irq, scc_isr, SA_INTERRUPT, "dmascc", info)) + return -EAGAIN; + } + + /* Request DMA if required */ + if (dev->dma && request_dma(dev->dma, "dmascc")) { + if (!info->open) free_irq(dev->irq, info); + return -EAGAIN; + } + + /* Initialize local variables */ + dev->tbusy = 0; + priv->rx_ptr = 0; + priv->rx_over = 0; + priv->rx_head = priv->rx_tail = priv->rx_count = 0; + priv->tx_state = TX_IDLE; + priv->tx_head = priv->tx_tail = priv->tx_count = 0; + priv->tx_ptr = 0; + priv->tx_sem = 0; + + /* Reset channel */ + write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV); + /* X1 clock, SDLC mode */ + write_scc(cmd, R4, SDLC | X1CLK); + /* DMA */ + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN); + /* 8 bit RX char, RX disable */ + write_scc(cmd, R3, Rx8); + /* 8 bit TX char, TX disable */ + write_scc(cmd, R5, Tx8); + /* SDLC address field */ + write_scc(cmd, R6, 0); + /* SDLC flag */ + write_scc(cmd, R7, FLAG); + switch (info->chip) { + case Z85C30: + /* Select WR7' */ + write_scc(cmd, R15, 1); + /* Auto EOM reset */ + write_scc(cmd, R7, 0x02); + write_scc(cmd, R15, 0); + break; + case Z85230: + /* Select WR7' */ + write_scc(cmd, R15, 1); + /* RX FIFO half full (interrupt only), Auto EOM reset, + TX FIFO empty (DMA only) */ + write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a); + write_scc(cmd, R15, 0); + break; + } + /* Preset CRC, NRZ(I) encoding */ + write_scc(cmd, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ)); + + /* Configure baud rate generator */ + if (priv->param.brg_tc >= 0) { + /* Program BR generator */ + write_scc(cmd, R12, priv->param.brg_tc & 0xFF); + write_scc(cmd, R13, (priv->param.brg_tc>>8) & 0xFF); + /* BRG source = SYS CLK; enable BRG; DTR REQ function (required by + PackeTwin, not connected on the PI2); set DPLL source to BRG */ + write_scc(cmd, R14, SSBR | DTRREQ | BRSRC | BRENABL); + /* Enable DPLL */ + write_scc(cmd, R14, SEARCH | DTRREQ | BRSRC | BRENABL); + } else { + /* Disable BR generator */ + write_scc(cmd, R14, DTRREQ | BRSRC); + } + + /* Configure clocks */ + if (info->type == TYPE_TWIN) { + /* Disable external TX clock receiver */ + outb_p((info->twin_serial_cfg &= + ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)), + io + TWIN_SERIAL_CFG); + } + write_scc(cmd, R11, priv->param.clocks); + if ((info->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) { + /* Enable external TX clock receiver */ + outb_p((info->twin_serial_cfg |= + (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)), + io + TWIN_SERIAL_CFG); + } + + /* Configure PackeTwin */ + if (info->type == TYPE_TWIN) { + /* Assert DTR, enable interrupts */ + outb_p((info->twin_serial_cfg |= TWIN_EI | + (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)), + io + TWIN_SERIAL_CFG); + } + + /* Read current status */ + priv->status = read_scc(cmd, R0); + /* Enable SYNC, DCD, and CTS interrupts */ + write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE); + + /* Configure PI2 DMA */ + if (info->type <= TYPE_PI2) outb_p(1, io + PI_DREQ_MASK); + + dev->start = 1; + info->open++; + MOD_INC_USE_COUNT; + + return 0; +} + + +static int scc_close(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int io = dev->base_addr; + int cmd = priv->cmd; + + dev->start = 0; + info->open--; + MOD_DEC_USE_COUNT; + + if (info->type == TYPE_TWIN) + /* Drop DTR */ + outb_p((info->twin_serial_cfg &= + (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)), + io + TWIN_SERIAL_CFG); + + /* Reset channel, free DMA */ + write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV); + if (dev->dma) { + if (info->type == TYPE_TWIN) outb_p(0, io + TWIN_DMA_CFG); + free_dma(dev->dma); + } + + if (!info->open) { + if (info->type <= TYPE_PI2) outb_p(0, io + PI_DREQ_MASK); + free_irq(dev->irq, info); + } + return 0; +} + + +static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + int rc; + struct scc_priv *priv = dev->priv; + + switch (cmd) { + case SIOCGSCCPARAM: + rc = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct scc_param)); + if (rc) return rc; + copy_to_user(ifr->ifr_data, &priv->param, sizeof(struct scc_param)); + return 0; + case SIOCSSCCPARAM: + if (!suser()) return -EPERM; + rc = verify_area(VERIFY_READ, ifr->ifr_data, sizeof(struct scc_param)); + if (rc) return rc; + if (dev->start) return -EAGAIN; + copy_from_user(&priv->param, ifr->ifr_data, sizeof(struct scc_param)); + dev->dma = priv->param.dma; + return 0; + default: + return -EINVAL; + } +} + + +static int scc_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + unsigned long flags; + int i; + + /* Block a timer-based transmit from overlapping */ + if (test_and_set_bit(0, (void *) &priv->tx_sem) != 0) { + atomic_inc((void *) &priv->stats.tx_dropped); + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + /* Return with an error if we cannot accept more data */ + if (dev->tbusy) { + priv->tx_sem = 0; + return -1; + } + + /* Transfer data to DMA buffer */ + i = priv->tx_head; + memcpy(priv->tx_buf[i], skb->data+1, skb->len-1); + priv->tx_len[i] = skb->len-1; + + save_flags(flags); + cli(); + + /* Set the busy flag if we just filled up the last buffer */ + priv->tx_head = (i + 1) % NUM_TX_BUF; + priv->tx_count++; + if (priv->tx_count == NUM_TX_BUF) dev->tbusy = 1; + + /* Set new TX state */ + if (priv->tx_state == TX_IDLE) { + /* Assert RTS, start timer */ + priv->tx_state = TX_TXDELAY; + if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK); + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } + + restore_flags(flags); + + dev_kfree_skb(skb, FREE_WRITE); + + priv->tx_sem = 0; + return 0; +} + + +static struct enet_statistics *scc_get_stats(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + + return &priv->stats; +} + + +static int scc_set_mac_address(struct device *dev, void *sa) +{ + memcpy(dev->dev_addr, ((struct sockaddr *)sa)->sa_data, dev->addr_len); + return 0; +} + + +static void scc_isr(int irq, void *dev_id, struct pt_regs * regs) +{ + struct scc_info *info = dev_id; + int is, io = info->dev[0].base_addr; + + /* We're a fast IRQ handler and are called with interrupts disabled */ + + /* IRQ sharing doesn't make sense due to ISA's edge-triggered + interrupts, hence it is safe to return if we have found and + processed a single device. */ + + /* Interrupt processing: We loop until we know that the IRQ line is + low. If another positive edge occurs afterwards during the ISR, + another interrupt will be triggered by the interrupt controller + as soon as the IRQ level is enabled again (see asm/irq.h). */ + + switch (info->type) { + case TYPE_PI: + case TYPE_PI2: + outb_p(0, io + PI_DREQ_MASK); + z8530_isr(info); + outb_p(1, io + PI_DREQ_MASK); + return; + case TYPE_TWIN: + while ((is = ~inb_p(io + TWIN_INT_REG)) & + TWIN_INT_MSK) { + if (is & TWIN_SCC_MSK) { + z8530_isr(info); + } else if (is & TWIN_TMR1_MSK) { + inb_p(io + TWIN_CLR_TMR1); + tm_isr(&info->dev[0]); + } else { + inb_p(io + TWIN_CLR_TMR2); + tm_isr(&info->dev[1]); + } + } + /* No interrupts pending from the PackeTwin */ + return; + } +} + + +static inline void z8530_isr(struct scc_info *info) +{ + int is, a_cmd; + + a_cmd = info->scc_base + SCCA_CMD; + + while ((is = read_scc(a_cmd, R3))) { + if (is & CHARxIP) { + rx_isr(&info->dev[0]); + } else if (is & CHATxIP) { + tx_isr(&info->dev[0]); + } else if (is & CHAEXT) { + es_isr(&info->dev[0]); + } else if (is & CHBRxIP) { + rx_isr(&info->dev[1]); + } else if (is & CHBTxIP) { + tx_isr(&info->dev[1]); + } else { + es_isr(&info->dev[1]); + } + } + /* Ok, no interrupts pending from this 8530. The INT line should + be inactive now. */ +} + + +static void rx_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + int cmd = priv->cmd; + + if (dev->dma) { + /* Check special condition and perform error reset. See 2.4.7.5. */ + special_condition(dev, read_scc(cmd, R1)); + write_scc(cmd, R0, ERR_RES); + } else { + /* Check special condition for each character. Error reset not necessary. + Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */ + int rc; + while (read_scc(cmd, R0) & Rx_CH_AV) { + rc = read_scc(cmd, R1); + if (priv->rx_ptr < BUF_SIZE) + priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8); + else { + priv->rx_over = 2; + read_scc(cmd, R8); + } + special_condition(dev, rc); + } + } +} + + +static void special_condition(struct device *dev, int rc) +{ + struct scc_priv *priv = dev->priv; + int cb, cmd = priv->cmd; + + /* See Figure 2-15. Only overrun and EOF need to be checked. */ + + if (rc & Rx_OVR) { + /* Receiver overrun */ + priv->rx_over = 1; + if (!dev->dma) write_scc(cmd, R0, ERR_RES); + } else if (rc & END_FR) { + /* End of frame. Get byte count */ + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + cb = BUF_SIZE - get_dma_residue(dev->dma) - 2; + } else { + cb = priv->rx_ptr - 2; + } + if (priv->rx_over) { + /* We had an overrun */ + priv->stats.rx_errors++; + if (priv->rx_over == 2) priv->stats.rx_length_errors++; + else priv->stats.rx_fifo_errors++; + priv->rx_over = 0; + } else if (rc & CRC_ERR) { + /* Count invalid CRC only if packet length >= minimum */ + if (cb >= 8) { + priv->stats.rx_errors++; + priv->stats.rx_crc_errors++; + } + } else { + if (cb >= 8) { + /* Put good frame in FIFO */ + priv->rx_len[priv->rx_head] = cb; + priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF; + priv->rx_count++; + if (priv->rx_count == NUM_RX_BUF) { + /* Disable receiver if FIFO full */ + write_scc(cmd, R3, Rx8); + priv->stats.rx_errors++; + priv->stats.rx_over_errors++; + } + /* Mark bottom half handler */ + queue_task(&priv->rx_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + /* Get ready for new frame */ + if (dev->dma) { + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + } else { + priv->rx_ptr = 0; + } + } +} + + +static void rx_bh(void *arg) +{ + struct device *dev = arg; + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + int i = priv->rx_tail; + int cb; + unsigned long flags; + struct sk_buff *skb; + unsigned char *data; + + save_flags(flags); + cli(); + + while (priv->rx_count) { + restore_flags(flags); + cb = priv->rx_len[i]; + /* Allocate buffer */ + skb = dev_alloc_skb(cb+1); + if (skb == NULL) { + /* Drop packet */ + priv->stats.rx_dropped++; + } else { + /* Fill buffer */ + data = skb_put(skb, cb+1); + data[0] = 0; + memcpy(&data[1], priv->rx_buf[i], cb); + skb->dev = dev; + skb->protocol = ntohs(ETH_P_AX25); + skb->mac.raw = skb->data; + netif_rx(skb); + priv->stats.rx_packets++; + } + save_flags(flags); + cli(); + /* Enable receiver if RX buffers have been unavailable */ + if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) { + if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK); + write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); + if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK); + } + /* Move tail */ + priv->rx_tail = i = (i + 1) % NUM_RX_BUF; + priv->rx_count--; + } + + restore_flags(flags); +} + + +static void tx_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + int cmd = priv->cmd; + int i = priv->tx_tail, p = priv->tx_ptr; + + /* Suspend TX interrupts if we don't want to send anything. + See Figure 2-22. */ + if (p == priv->tx_len[i]) { + write_scc(cmd, R0, RES_Tx_P); + return; + } + + /* Write characters */ + while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) { + write_scc(cmd, R8, priv->tx_buf[i][p++]); + } + priv->tx_ptr = p; + +} + + +static void es_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int i, cmd = priv->cmd; + int st, dst, res; + + /* Read status and reset interrupt bit */ + st = read_scc(cmd, R0); + write_scc(cmd, R0, RES_EXT_INT); + dst = priv->status ^ st; + priv->status = st; + + /* Since the EOM latch is reset automatically, we assume that + it has been zero if and only if we are in the TX_ACTIVE state. + Otherwise we follow 2.4.9.6. */ + + /* Transmit underrun */ + if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) { + /* Get remaining bytes */ + i = priv->tx_tail; + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + res = get_dma_residue(dev->dma); + } else { + res = priv->tx_len[i] - priv->tx_ptr; + if (res) write_scc(cmd, R0, RES_Tx_P); + priv->tx_ptr = 0; + } + /* Remove frame from FIFO */ + priv->tx_tail = (i + 1) % NUM_TX_BUF; + priv->tx_count--; + dev->tbusy = 0; + /* Check if another frame is available and we are allowed to transmit */ + if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) { + if (dev->dma) { + set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); + set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); + enable_dma(dev->dma); + } else { + /* If we have an ESCC, we are allowed to write data bytes + immediately. Otherwise we have to wait for the next + TX interrupt. See Figure 2-22. */ + if (info->chip == Z85230) { + tx_isr(dev); + } + } + } else { + /* No frame available. Disable interrupts. */ + priv->tx_state = TX_SQDELAY; + delay(dev, priv->param.sqdelay); + write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN); + } + /* Update packet statistics */ + if (res) { + priv->stats.tx_errors++; + priv->stats.tx_fifo_errors++; + } else { + priv->stats.tx_packets++; + } + /* Inform upper layers */ + mark_bh(NET_BH); + } + + /* DCD transition */ + if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) { + /* Transmitter state change */ + priv->tx_state = TX_OFF; + /* Enable or disable receiver */ + if (st & DCD) { + if (dev->dma) { + /* Program DMA controller */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_READ); + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + /* Configure PackeTwin DMA */ + if (info->type == TYPE_TWIN) { + outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3, + dev->base_addr + TWIN_DMA_CFG); + } + /* Sp. cond. intr. only, ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx | + WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB); + } else { + /* Intr. on all Rx characters and Sp. cond., ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT | + WT_FN_RDYFN); + } + if (priv->rx_count < NUM_RX_BUF) { + /* Enable receiver */ + write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); + } + } else { + /* Disable DMA */ + if (dev->dma) disable_dma(dev->dma); + /* Disable receiver */ + write_scc(cmd, R3, Rx8); + /* DMA disable, RX int disable, Ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); + /* Transmitter state change */ + if (random() > priv->param.persist) + delay(dev, priv->param.slottime); + else { + if (priv->tx_count) { + priv->tx_state = TX_TXDELAY; + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } else { + priv->tx_state = TX_IDLE; + } + } + } + } + + /* CTS transition */ + if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) { + /* Timer has expired */ + tm_isr(dev); + } + + /* /SYNC/HUNT transition */ + if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) { + /* Reset current frame and clear RX FIFO */ + while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8); + priv->rx_over = 0; + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + } else { + priv->rx_ptr = 0; + } + } +} + + +static void tm_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + + switch (priv->tx_state) { + case TX_OFF: + if (~priv->status & DCD) { + if (random() > priv->param.persist) delay(dev, priv->param.slottime); + else { + if (priv->tx_count) { + priv->tx_state = TX_TXDELAY; + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } else { + priv->tx_state = TX_IDLE; + } + } + } + break; + case TX_TXDELAY: + priv->tx_state = TX_ACTIVE; + if (dev->dma) { + /* Program DMA controller */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_WRITE); + set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); + set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); + enable_dma(dev->dma); + /* Configure PackeTwin DMA */ + if (info->type == TYPE_TWIN) { + outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3, + dev->base_addr + TWIN_DMA_CFG); + } + /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin + is used for TX DMA requests, but we enable the WAIT/DMA request + pin, anyway */ + write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB); + } else { + write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB); + tx_isr(dev); + } + if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L); + break; + case TX_SQDELAY: + /* Disable transmitter */ + write_scc(cmd, R5, TxCRC_ENAB | Tx8); + /* Transmitter state change: Switch to TX_OFF and wait at least + 1 slottime. */ + priv->tx_state = TX_OFF; + if (~priv->status & DCD) delay(dev, priv->param.waittime); + } +} + + +static inline void delay(struct device *dev, int t) +{ + struct scc_priv *priv = dev->priv; + int tmr = priv->tmr; + + outb_p(t & 0xFF, tmr); + outb_p((t >> 8) & 0xFF, tmr); +} + + +static inline unsigned char random(void) +{ + /* See "Numerical Recipes in C", second edition, p. 284 */ + rand = rand * 1664525L + 1013904223L; + return (unsigned char) (rand >> 24); +} + + diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/hdlcdrv.c +++ /dev/null diff --git a/drivers/net/mkiss.c b/drivers/net/mkiss.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/mkiss.c +++ /dev/null diff --git a/drivers/net/mkiss.h b/drivers/net/mkiss.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/mkiss.h +++ /dev/null diff --git a/drivers/net/pi2.c b/drivers/net/pi2.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/pi2.c +++ /dev/null diff --git a/drivers/net/pt.c b/drivers/net/pt.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/pt.c +++ /dev/null diff --git a/drivers/net/scc.c b/drivers/net/scc.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/scc.c +++ /dev/null diff --git a/drivers/net/soundmodem/.cvsignore b/drivers/net/soundmodem/.cvsignore deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/.cvsignore +++ /dev/null diff --git a/drivers/net/soundmodem/Makefile b/drivers/net/soundmodem/Makefile deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/Makefile +++ /dev/null diff --git a/drivers/net/soundmodem/gentbl.c b/drivers/net/soundmodem/gentbl.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/gentbl.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm.c b/drivers/net/soundmodem/sm.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm.h b/drivers/net/soundmodem/sm.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm.h +++ /dev/null diff --git a/drivers/net/soundmodem/sm_afsk1200.c b/drivers/net/soundmodem/sm_afsk1200.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_afsk1200.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_afsk2400_7.c b/drivers/net/soundmodem/sm_afsk2400_7.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_afsk2400_7.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_afsk2400_8.c b/drivers/net/soundmodem/sm_afsk2400_8.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_afsk2400_8.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_fsk9600.c b/drivers/net/soundmodem/sm_fsk9600.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_fsk9600.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_hapn4800.c b/drivers/net/soundmodem/sm_hapn4800.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_hapn4800.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_sbc.c b/drivers/net/soundmodem/sm_sbc.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_sbc.c +++ /dev/null diff --git a/drivers/net/soundmodem/sm_wss.c b/drivers/net/soundmodem/sm_wss.c deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/sm_wss.c +++ /dev/null diff --git a/drivers/net/soundmodem/smdma.h b/drivers/net/soundmodem/smdma.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/soundmodem/smdma.h +++ /dev/null diff --git a/drivers/net/z8530.h b/drivers/net/z8530.h deleted file mode 100644 index e69de29bb..000000000 --- a/drivers/net/z8530.h +++ /dev/null diff --git a/drivers/sgi/char/Config.in b/drivers/sgi/char/Config.in new file mode 100644 index 000000000..e216e4328 --- /dev/null +++ b/drivers/sgi/char/Config.in @@ -0,0 +1,12 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'SGI Character devices' + +bool 'SGI Zilog85C30 serial support' CONFIG_SGI_SERIAL +if [ "$CONFIG_SGI_SERIAL" != "n" ]; then + define_bool CONFIG_SERIAL y +fi + +endmenu |