📄 parport_ip32.c
字号:
/* Low-level parallel port routines for built-in port on SGI IP32 * * Author: Arnaud Giersch <arnaud.giersch@free.fr> * * Based on parport_pc.c by * Phil Blundell, Tim Waugh, Jose Renau, David Campbell, * Andrea Arcangeli, et al. * * Thanks to Ilya A. Volynets-Evenbakh for his help. * * Copyright (C) 2005, 2006 Arnaud Giersch. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Current status: * * Basic SPP and PS2 modes are supported. * Support for parallel port IRQ is present. * Hardware SPP (a.k.a. compatibility), EPP, and ECP modes are * supported. * SPP/ECP FIFO can be driven in PIO or DMA mode. PIO mode can work with * or without interrupt support. * * Hardware ECP mode is not fully implemented (ecp_read_data and * ecp_write_addr are actually missing). * * To do: * * Fully implement ECP mode. * EPP and ECP mode need to be tested. I currently do not own any * peripheral supporting these extended mode, and cannot test them. * If DMA mode works well, decide if support for PIO FIFO modes should be * dropped. * Use the io{read,write} family functions when they become available in * the linux-mips.org tree. Note: the MIPS specific functions readsb() * and writesb() are to be translated by ioread8_rep() and iowrite8_rep() * respectively. *//* The built-in parallel port on the SGI 02 workstation (a.k.a. IP32) is an * IEEE 1284 parallel port driven by a Texas Instrument TL16PIR552PH chip[1]. * This chip supports SPP, bidirectional, EPP and ECP modes. It has a 16 byte * FIFO buffer and supports DMA transfers. * * [1] http://focus.ti.com/docs/prod/folders/print/tl16pir552.html * * Theoretically, we could simply use the parport_pc module. It is however * not so simple. The parport_pc code assumes that the parallel port * registers are port-mapped. On the O2, they are memory-mapped. * Furthermore, each register is replicated on 256 consecutive addresses (as * it is for the built-in serial ports on the same chip). *//*--- Some configuration defines ---------------------------------------*//* DEBUG_PARPORT_IP32 * 0 disable debug * 1 standard level: pr_debug1 is enabled * 2 parport_ip32_dump_state is enabled * >=3 verbose level: pr_debug is enabled */#if !defined(DEBUG_PARPORT_IP32)# define DEBUG_PARPORT_IP32 0 /* 0 (disabled) for production */#endif/*----------------------------------------------------------------------*//* Setup DEBUG macros. This is done before any includes, just in case we * activate pr_debug() with DEBUG_PARPORT_IP32 >= 3. */#if DEBUG_PARPORT_IP32 == 1# warning DEBUG_PARPORT_IP32 == 1#elif DEBUG_PARPORT_IP32 == 2# warning DEBUG_PARPORT_IP32 == 2#elif DEBUG_PARPORT_IP32 >= 3# warning DEBUG_PARPORT_IP32 >= 3# if !defined(DEBUG)# define DEBUG /* enable pr_debug() in kernel.h */# endif#endif#include <linux/completion.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/err.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/jiffies.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/parport.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/stddef.h>#include <linux/types.h>#include <asm/io.h>#include <asm/ip32/ip32_ints.h>#include <asm/ip32/mace.h>/*--- Global variables -------------------------------------------------*//* Verbose probing on by default for debugging. */#if DEBUG_PARPORT_IP32 >= 1# define DEFAULT_VERBOSE_PROBING 1#else# define DEFAULT_VERBOSE_PROBING 0#endif/* Default prefix for printk */#define PPIP32 "parport_ip32: "/* * These are the module parameters: * @features: bit mask of features to enable/disable * (all enabled by default) * @verbose_probing: log chit-chat during initialization */#define PARPORT_IP32_ENABLE_IRQ (1U << 0)#define PARPORT_IP32_ENABLE_DMA (1U << 1)#define PARPORT_IP32_ENABLE_SPP (1U << 2)#define PARPORT_IP32_ENABLE_EPP (1U << 3)#define PARPORT_IP32_ENABLE_ECP (1U << 4)static unsigned int features = ~0U;static int verbose_probing = DEFAULT_VERBOSE_PROBING;/* We do not support more than one port. */static struct parport *this_port = NULL;/* Timing constants for FIFO modes. */#define FIFO_NFAULT_TIMEOUT 100 /* milliseconds */#define FIFO_POLLING_INTERVAL 50 /* microseconds *//*--- I/O register definitions -----------------------------------------*//** * struct parport_ip32_regs - virtual addresses of parallel port registers * @data: Data Register * @dsr: Device Status Register * @dcr: Device Control Register * @eppAddr: EPP Address Register * @eppData0: EPP Data Register 0 * @eppData1: EPP Data Register 1 * @eppData2: EPP Data Register 2 * @eppData3: EPP Data Register 3 * @ecpAFifo: ECP Address FIFO * @fifo: General FIFO register. The same address is used for: * - cFifo, the Parallel Port DATA FIFO * - ecpDFifo, the ECP Data FIFO * - tFifo, the ECP Test FIFO * @cnfgA: Configuration Register A * @cnfgB: Configuration Register B * @ecr: Extended Control Register */struct parport_ip32_regs { void __iomem *data; void __iomem *dsr; void __iomem *dcr; void __iomem *eppAddr; void __iomem *eppData0; void __iomem *eppData1; void __iomem *eppData2; void __iomem *eppData3; void __iomem *ecpAFifo; void __iomem *fifo; void __iomem *cnfgA; void __iomem *cnfgB; void __iomem *ecr;};/* Device Status Register */#define DSR_nBUSY (1U << 7) /* PARPORT_STATUS_BUSY */#define DSR_nACK (1U << 6) /* PARPORT_STATUS_ACK */#define DSR_PERROR (1U << 5) /* PARPORT_STATUS_PAPEROUT */#define DSR_SELECT (1U << 4) /* PARPORT_STATUS_SELECT */#define DSR_nFAULT (1U << 3) /* PARPORT_STATUS_ERROR */#define DSR_nPRINT (1U << 2) /* specific to TL16PIR552 *//* #define DSR_reserved (1U << 1) */#define DSR_TIMEOUT (1U << 0) /* EPP timeout *//* Device Control Register *//* #define DCR_reserved (1U << 7) | (1U << 6) */#define DCR_DIR (1U << 5) /* direction */#define DCR_IRQ (1U << 4) /* interrupt on nAck */#define DCR_SELECT (1U << 3) /* PARPORT_CONTROL_SELECT */#define DCR_nINIT (1U << 2) /* PARPORT_CONTROL_INIT */#define DCR_AUTOFD (1U << 1) /* PARPORT_CONTROL_AUTOFD */#define DCR_STROBE (1U << 0) /* PARPORT_CONTROL_STROBE *//* ECP Configuration Register A */#define CNFGA_IRQ (1U << 7)#define CNFGA_ID_MASK ((1U << 6) | (1U << 5) | (1U << 4))#define CNFGA_ID_SHIFT 4#define CNFGA_ID_16 (00U << CNFGA_ID_SHIFT)#define CNFGA_ID_8 (01U << CNFGA_ID_SHIFT)#define CNFGA_ID_32 (02U << CNFGA_ID_SHIFT)/* #define CNFGA_reserved (1U << 3) */#define CNFGA_nBYTEINTRANS (1U << 2)#define CNFGA_PWORDLEFT ((1U << 1) | (1U << 0))/* ECP Configuration Register B */#define CNFGB_COMPRESS (1U << 7)#define CNFGB_INTRVAL (1U << 6)#define CNFGB_IRQ_MASK ((1U << 5) | (1U << 4) | (1U << 3))#define CNFGB_IRQ_SHIFT 3#define CNFGB_DMA_MASK ((1U << 2) | (1U << 1) | (1U << 0))#define CNFGB_DMA_SHIFT 0/* Extended Control Register */#define ECR_MODE_MASK ((1U << 7) | (1U << 6) | (1U << 5))#define ECR_MODE_SHIFT 5#define ECR_MODE_SPP (00U << ECR_MODE_SHIFT)#define ECR_MODE_PS2 (01U << ECR_MODE_SHIFT)#define ECR_MODE_PPF (02U << ECR_MODE_SHIFT)#define ECR_MODE_ECP (03U << ECR_MODE_SHIFT)#define ECR_MODE_EPP (04U << ECR_MODE_SHIFT)/* #define ECR_MODE_reserved (05U << ECR_MODE_SHIFT) */#define ECR_MODE_TST (06U << ECR_MODE_SHIFT)#define ECR_MODE_CFG (07U << ECR_MODE_SHIFT)#define ECR_nERRINTR (1U << 4)#define ECR_DMAEN (1U << 3)#define ECR_SERVINTR (1U << 2)#define ECR_F_FULL (1U << 1)#define ECR_F_EMPTY (1U << 0)/*--- Private data -----------------------------------------------------*//** * enum parport_ip32_irq_mode - operation mode of interrupt handler * @PARPORT_IP32_IRQ_FWD: forward interrupt to the upper parport layer * @PARPORT_IP32_IRQ_HERE: interrupt is handled locally */enum parport_ip32_irq_mode { PARPORT_IP32_IRQ_FWD, PARPORT_IP32_IRQ_HERE };/** * struct parport_ip32_private - private stuff for &struct parport * @regs: register addresses * @dcr_cache: cached contents of DCR * @dcr_writable: bit mask of writable DCR bits * @pword: number of bytes per PWord * @fifo_depth: number of PWords that FIFO will hold * @readIntrThreshold: minimum number of PWords we can read * if we get an interrupt * @writeIntrThreshold: minimum number of PWords we can write * if we get an interrupt * @irq_mode: operation mode of interrupt handler for this port * @irq_complete: mutex used to wait for an interrupt to occur */struct parport_ip32_private { struct parport_ip32_regs regs; unsigned int dcr_cache; unsigned int dcr_writable; unsigned int pword; unsigned int fifo_depth; unsigned int readIntrThreshold; unsigned int writeIntrThreshold; enum parport_ip32_irq_mode irq_mode; struct completion irq_complete;};/*--- Debug code -------------------------------------------------------*//* * pr_debug1 - print debug messages * * This is like pr_debug(), but is defined for %DEBUG_PARPORT_IP32 >= 1 */#if DEBUG_PARPORT_IP32 >= 1# define pr_debug1(...) printk(KERN_DEBUG __VA_ARGS__)#else /* DEBUG_PARPORT_IP32 < 1 */# define pr_debug1(...) do { } while (0)#endif/* * pr_trace, pr_trace1 - trace function calls * @p: pointer to &struct parport * @fmt: printk format string * @...: parameters for format string * * Macros used to trace function calls. The given string is formatted after * function name. pr_trace() uses pr_debug(), and pr_trace1() uses * pr_debug1(). __pr_trace() is the low-level macro and is not to be used * directly. */#define __pr_trace(pr, p, fmt, ...) \ pr("%s: %s" fmt "\n", \ ({ const struct parport *__p = (p); \ __p ? __p->name : "parport_ip32"; }), \ __func__ , ##__VA_ARGS__)#define pr_trace(p, fmt, ...) __pr_trace(pr_debug, p, fmt , ##__VA_ARGS__)#define pr_trace1(p, fmt, ...) __pr_trace(pr_debug1, p, fmt , ##__VA_ARGS__)/* * __pr_probe, pr_probe - print message if @verbose_probing is true * @p: pointer to &struct parport * @fmt: printk format string * @...: parameters for format string * * For new lines, use pr_probe(). Use __pr_probe() for continued lines. */#define __pr_probe(...) \ do { if (verbose_probing) printk(__VA_ARGS__); } while (0)#define pr_probe(p, fmt, ...) \ __pr_probe(KERN_INFO PPIP32 "0x%lx: " fmt, (p)->base , ##__VA_ARGS__)/* * parport_ip32_dump_state - print register status of parport * @p: pointer to &struct parport * @str: string to add in message * @show_ecp_config: shall we dump ECP configuration registers too? * * This function is only here for debugging purpose, and should be used with * care. Reading the parallel port registers may have undesired side effects. * Especially if @show_ecp_config is true, the parallel port is resetted. * This function is only defined if %DEBUG_PARPORT_IP32 >= 2. */#if DEBUG_PARPORT_IP32 >= 2static void parport_ip32_dump_state(struct parport *p, char *str, unsigned int show_ecp_config){ struct parport_ip32_private * const priv = p->physport->private_data; unsigned int i; printk(KERN_DEBUG PPIP32 "%s: state (%s):\n", p->name, str); { static const char ecr_modes[8][4] = {"SPP", "PS2", "PPF", "ECP", "EPP", "???", "TST", "CFG"}; unsigned int ecr = readb(priv->regs.ecr); printk(KERN_DEBUG PPIP32 " ecr=0x%02x", ecr); printk(" %s", ecr_modes[(ecr & ECR_MODE_MASK) >> ECR_MODE_SHIFT]); if (ecr & ECR_nERRINTR) printk(",nErrIntrEn"); if (ecr & ECR_DMAEN) printk(",dmaEn"); if (ecr & ECR_SERVINTR) printk(",serviceIntr"); if (ecr & ECR_F_FULL) printk(",f_full"); if (ecr & ECR_F_EMPTY) printk(",f_empty"); printk("\n"); } if (show_ecp_config) { unsigned int oecr, cnfgA, cnfgB; oecr = readb(priv->regs.ecr); writeb(ECR_MODE_PS2, priv->regs.ecr); writeb(ECR_MODE_CFG, priv->regs.ecr); cnfgA = readb(priv->regs.cnfgA); cnfgB = readb(priv->regs.cnfgB); writeb(ECR_MODE_PS2, priv->regs.ecr); writeb(oecr, priv->regs.ecr); printk(KERN_DEBUG PPIP32 " cnfgA=0x%02x", cnfgA); printk(" ISA-%s", (cnfgA & CNFGA_IRQ) ? "Level" : "Pulses"); switch (cnfgA & CNFGA_ID_MASK) { case CNFGA_ID_8: printk(",8 bits"); break; case CNFGA_ID_16: printk(",16 bits"); break; case CNFGA_ID_32: printk(",32 bits"); break; default: printk(",unknown ID"); break; } if (!(cnfgA & CNFGA_nBYTEINTRANS)) printk(",ByteInTrans"); if ((cnfgA & CNFGA_ID_MASK) != CNFGA_ID_8) printk(",%d byte%s left", cnfgA & CNFGA_PWORDLEFT, ((cnfgA & CNFGA_PWORDLEFT) > 1) ? "s" : ""); printk("\n"); printk(KERN_DEBUG PPIP32 " cnfgB=0x%02x", cnfgB); printk(" irq=%u,dma=%u", (cnfgB & CNFGB_IRQ_MASK) >> CNFGB_IRQ_SHIFT, (cnfgB & CNFGB_DMA_MASK) >> CNFGB_DMA_SHIFT); printk(",intrValue=%d", !!(cnfgB & CNFGB_INTRVAL)); if (cnfgB & CNFGB_COMPRESS) printk(",compress"); printk("\n"); } for (i = 0; i < 2; i++) { unsigned int dcr = i ? priv->dcr_cache : readb(priv->regs.dcr); printk(KERN_DEBUG PPIP32 " dcr(%s)=0x%02x", i ? "soft" : "hard", dcr); printk(" %s", (dcr & DCR_DIR) ? "rev" : "fwd"); if (dcr & DCR_IRQ) printk(",ackIntEn"); if (!(dcr & DCR_SELECT)) printk(",nSelectIn"); if (dcr & DCR_nINIT) printk(",nInit"); if (!(dcr & DCR_AUTOFD)) printk(",nAutoFD"); if (!(dcr & DCR_STROBE)) printk(",nStrobe"); printk("\n"); }#define sep (f++ ? ',' : ' ') { unsigned int f = 0; unsigned int dsr = readb(priv->regs.dsr); printk(KERN_DEBUG PPIP32 " dsr=0x%02x", dsr); if (!(dsr & DSR_nBUSY)) printk("%cBusy", sep); if (dsr & DSR_nACK) printk("%cnAck", sep); if (dsr & DSR_PERROR) printk("%cPError", sep); if (dsr & DSR_SELECT) printk("%cSelect", sep); if (dsr & DSR_nFAULT)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -