📄 sdladrv.c
字号:
/****************************************************************************** sdladrv.c SDLA Support Module. Main module.** This module is a library of common hardware-specific functions* used by all Sangoma drivers.** Author: Gideon Hack ** Copyright: (c) 1995-2000 Sangoma Technologies Inc.** 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.* ============================================================================* Mar 20, 2001 Nenad Corbic Added the auto_pci_cfg filed, to support* the PCISLOT #0. * Apr 04, 2000 Nenad Corbic Fixed the auto memory detection code.* The memory test at address 0xC8000.* Mar 09, 2000 Nenad Corbic Added Gideon's Bug Fix: clear pci* interrupt flags on initial load.* Jun 02, 1999 Gideon Hack Added support for the S514 adapter.* Updates for Linux 2.2.X kernels. * Sep 17, 1998 Jaspreet Singh Updates for linux 2.2.X kernels* Dec 20, 1996 Gene Kozin Version 3.0.0. Complete overhaul.* Jul 12, 1996 Gene Kozin Changes for Linux 2.0 compatibility.* Jun 12, 1996 Gene Kozin Added support for S503 card.* Apr 30, 1996 Gene Kozin SDLA hardware interrupt is acknowledged before* calling protocolspecific ISR.* Register I/O ports with Linux kernel.* Miscellaneous bug fixes.* Dec 20, 1995 Gene Kozin Fixed a bug in interrupt routine.* Oct 14, 1995 Gene Kozin Initial version.*****************************************************************************//***************************************************************************** * Notes: * ------ * 1. This code is ment to be system-independent (as much as possible). To * achive this, various macros are used to hide system-specific interfaces. * To compile this code, one of the following constants must be defined: * * Platform Define * -------- ------ * Linux _LINUX_ * SCO Unix _SCO_UNIX_ * * 2. Supported adapter types: * * S502A * ES502A (S502E) * S503 * S507 * S508 (S509) * * 3. S502A Notes: * * There is no separate DPM window enable/disable control in S502A. It * opens immediately after a window number it written to the HMCR * register. To close the window, HMCR has to be written a value * ????1111b (e.g. 0x0F or 0xFF). * * S502A DPM window cannot be located at offset E000 (e.g. 0xAE000). * * There should be a delay of ??? before reading back S502A status * register. * * 4. S502E Notes: * * S502E has a h/w bug: although default IRQ line state is HIGH, enabling * interrupts by setting bit 1 of the control register (BASE) to '1' * causes it to go LOW! Therefore, disabling interrupts by setting that * bit to '0' causes low-to-high transition on IRQ line (ghosty * interrupt). The same occurs when disabling CPU by resetting bit 0 of * CPU control register (BASE+3) - see the next note. * * S502E CPU and DPM control is limited: * * o CPU cannot be stopped independently. Resetting bit 0 of the CPUi * control register (BASE+3) shuts the board down entirely, including * DPM; * * o DPM access cannot be controlled dynamically. Ones CPU is started, * bit 1 of the control register (BASE) is used to enable/disable IRQ, * so that access to shared memory cannot be disabled while CPU is * running. ****************************************************************************/#define _LINUX_#if defined(_LINUX_) /****** Linux *******************************/#include <linux/config.h>#include <linux/kernel.h> /* printk(), and other useful stuff */#include <linux/stddef.h> /* offsetof(), etc. */#include <linux/errno.h> /* return codes */#include <linux/string.h> /* inline memset(), etc. */#include <linux/module.h> /* support for loadable modules */#include <linux/jiffies.h> /* for jiffies, HZ, etc. */#include <linux/sdladrv.h> /* API definitions */#include <linux/sdlasfm.h> /* SDLA firmware module definitions */#include <linux/sdlapci.h> /* SDLA PCI hardware definitions */#include <linux/pci.h> /* PCI defines and function prototypes */#include <asm/io.h> /* for inb(), outb(), etc. */#define _INB(port) (inb(port))#define _OUTB(port, byte) (outb((byte),(port)))#define SYSTEM_TICK jiffies#include <linux/init.h>#elif defined(_SCO_UNIX_) /****** SCO Unix ****************************/#if !defined(INKERNEL)#error This code MUST be compiled in kernel mode!#endif#include <sys/sdladrv.h> /* API definitions */#include <sys/sdlasfm.h> /* SDLA firmware module definitions */#include <sys/inline.h> /* for inb(), outb(), etc. */#define _INB(port) (inb(port))#define _OUTB(port, byte) (outb((port),(byte)))#define SYSTEM_TICK lbolt#else#error Unknown system type!#endif#define MOD_VERSION 3#define MOD_RELEASE 0#define SDLA_IODELAY 100 /* I/O Rd/Wr delay, 10 works for 486DX2-66 */#define EXEC_DELAY 20 /* shared memory access delay, mks */#define EXEC_TIMEOUT (HZ*2) /* command timeout, in ticks *//* I/O port address range */#define S502A_IORANGE 3#define S502E_IORANGE 4#define S503_IORANGE 3#define S507_IORANGE 4#define S508_IORANGE 4/* Maximum amount of memory */#define S502_MAXMEM 0x10000L#define S503_MAXMEM 0x10000L#define S507_MAXMEM 0x40000L#define S508_MAXMEM 0x40000L/* Minimum amount of memory */#define S502_MINMEM 0x8000L#define S503_MINMEM 0x8000L#define S507_MINMEM 0x20000L#define S508_MINMEM 0x20000L#define NO_PORT -1/****** Function Prototypes *************************************************//* Hardware-specific functions */static int sdla_detect (sdlahw_t* hw);static int sdla_autodpm (sdlahw_t* hw);static int sdla_setdpm (sdlahw_t* hw);static int sdla_load (sdlahw_t* hw, sfm_t* sfm, unsigned len);static int sdla_init (sdlahw_t* hw);static unsigned long sdla_memtest (sdlahw_t* hw);static int sdla_bootcfg (sdlahw_t* hw, sfm_info_t* sfminfo);static unsigned char make_config_byte (sdlahw_t* hw);static int sdla_start (sdlahw_t* hw, unsigned addr);static int init_s502a (sdlahw_t* hw);static int init_s502e (sdlahw_t* hw);static int init_s503 (sdlahw_t* hw);static int init_s507 (sdlahw_t* hw);static int init_s508 (sdlahw_t* hw); static int detect_s502a (int port);static int detect_s502e (int port);static int detect_s503 (int port);static int detect_s507 (int port);static int detect_s508 (int port);static int detect_s514 (sdlahw_t* hw);static int find_s514_adapter(sdlahw_t* hw, char find_first_S514_card);/* Miscellaneous functions */static void peek_by_4 (unsigned long src, void* buf, unsigned len);static void poke_by_4 (unsigned long dest, void* buf, unsigned len);static int calibrate_delay (int mks);static int get_option_index (unsigned* optlist, unsigned optval);static unsigned check_memregion (void* ptr, unsigned len);static unsigned test_memregion (void* ptr, unsigned len);static unsigned short checksum (unsigned char* buf, unsigned len);static int init_pci_slot(sdlahw_t *);static int pci_probe(sdlahw_t *hw);/****** Global Data ********************************************************** * Note: All data must be explicitly initialized!!! */static struct pci_device_id sdladrv_pci_tbl[] = { { V3_VENDOR_ID, V3_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(pci, sdladrv_pci_tbl);MODULE_LICENSE("GPL");/* private data */static char modname[] = "sdladrv";static char fullname[] = "SDLA Support Module";static char copyright[] = "(c) 1995-1999 Sangoma Technologies Inc.";static unsigned exec_idle;/* Hardware configuration options. * These are arrays of configuration options used by verification routines. * The first element of each array is its size (i.e. number of options). */static unsigned s502_port_options[] = { 4, 0x250, 0x300, 0x350, 0x360 };static unsigned s503_port_options[] = { 8, 0x250, 0x254, 0x300, 0x304, 0x350, 0x354, 0x360, 0x364 };static unsigned s508_port_options[] = { 8, 0x250, 0x270, 0x280, 0x300, 0x350, 0x360, 0x380, 0x390 };static unsigned s502a_irq_options[] = { 0 };static unsigned s502e_irq_options[] = { 4, 2, 3, 5, 7 };static unsigned s503_irq_options[] = { 5, 2, 3, 4, 5, 7 };static unsigned s508_irq_options[] = { 8, 3, 4, 5, 7, 10, 11, 12, 15 };static unsigned s502a_dpmbase_options[] ={ 28, 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000,};static unsigned s507_dpmbase_options[] ={ 32, 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000, 0xB0000, 0xB2000, 0xB4000, 0xB6000, 0xB8000, 0xBA000, 0xBC000, 0xBE000, 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000, 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000,};static unsigned s508_dpmbase_options[] = /* incl. S502E and S503 */{ 32, 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000, 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000, 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000, 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000,};/*static unsigned s502_dpmsize_options[] = { 2, 0x2000, 0x10000 };static unsigned s507_dpmsize_options[] = { 2, 0x2000, 0x4000 };static unsigned s508_dpmsize_options[] = { 1, 0x2000 };*/static unsigned s502a_pclk_options[] = { 2, 3600, 7200 };static unsigned s502e_pclk_options[] = { 5, 3600, 5000, 7200, 8000, 10000 };static unsigned s503_pclk_options[] = { 3, 7200, 8000, 10000 };static unsigned s507_pclk_options[] = { 1, 12288 };static unsigned s508_pclk_options[] = { 1, 16000 };/* Host memory control register masks */static unsigned char s502a_hmcr[] ={ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, /* A0000 - AC000 */ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, /* C0000 - CC000 */ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, /* D0000 - DC000 */ 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, /* E0000 - EC000 */};static unsigned char s502e_hmcr[] ={ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, /* A0000 - AE000 */ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, /* C0000 - CE000 */ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, /* D0000 - DE000 */ 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, /* E0000 - EE000 */};static unsigned char s507_hmcr[] ={ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, /* A0000 - AE000 */ 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, /* B0000 - BE000 */ 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, /* C0000 - CE000 */ 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, /* E0000 - EE000 */};static unsigned char s508_hmcr[] ={ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* A0000 - AE000 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* C0000 - CE000 */ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* D0000 - DE000 */ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* E0000 - EE000 */};static unsigned char s507_irqmask[] ={ 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0};static int pci_slot_ar[MAX_S514_CARDS];/******* Kernel Loadable Module Entry Points ********************************//*============================================================================ * Module 'insert' entry point. * o print announcement * o initialize static data * o calibrate SDLA shared memory access delay. * * Return: 0 Ok * < 0 error. * Context: process */static int __init sdladrv_init(void){ int i=0; printk(KERN_INFO "%s v%u.%u %s\n", fullname, MOD_VERSION, MOD_RELEASE, copyright); exec_idle = calibrate_delay(EXEC_DELAY);#ifdef WANDEBUG printk(KERN_DEBUG "%s: exec_idle = %d\n", modname, exec_idle);#endif /* Initialize the PCI Card array, which * will store flags, used to mark * card initialization state */ for (i=0; i<MAX_S514_CARDS; i++) pci_slot_ar[i] = 0xFF; return 0;}/*============================================================================ * Module 'remove' entry point. * o release all remaining system resources */static void __exit sdladrv_cleanup(void){}module_init(sdladrv_init);module_exit(sdladrv_cleanup);/******* Kernel APIs ********************************************************//*============================================================================ * Set up adapter. * o detect adapter type * o verify hardware configuration options * o check for hardware conflicts * o set up adapter shared memory * o test adapter memory * o load firmware * Return: 0 ok. * < 0 error */EXPORT_SYMBOL(sdla_setup);int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len){ unsigned* irq_opt = NULL; /* IRQ options */ unsigned* dpmbase_opt = NULL; /* DPM window base options */ unsigned* pclk_opt = NULL; /* CPU clock rate options */ int err=0; if (sdla_detect(hw)) { if(hw->type != SDLA_S514) printk(KERN_INFO "%s: no SDLA card found at port 0x%X\n", modname, hw->port); return -EINVAL; } if(hw->type != SDLA_S514) { printk(KERN_INFO "%s: found S%04u card at port 0x%X.\n", modname, hw->type, hw->port); hw->dpmsize = SDLA_WINDOWSIZE; switch (hw->type) { case SDLA_S502A: hw->io_range = S502A_IORANGE; irq_opt = s502a_irq_options; dpmbase_opt = s502a_dpmbase_options; pclk_opt = s502a_pclk_options; break; case SDLA_S502E: hw->io_range = S502E_IORANGE; irq_opt = s502e_irq_options; dpmbase_opt = s508_dpmbase_options; pclk_opt = s502e_pclk_options; break; case SDLA_S503: hw->io_range = S503_IORANGE; irq_opt = s503_irq_options; dpmbase_opt = s508_dpmbase_options; pclk_opt = s503_pclk_options; break; case SDLA_S507: hw->io_range = S507_IORANGE; irq_opt = s508_irq_options; dpmbase_opt = s507_dpmbase_options; pclk_opt = s507_pclk_options; break; case SDLA_S508: hw->io_range = S508_IORANGE; irq_opt = s508_irq_options; dpmbase_opt = s508_dpmbase_options; pclk_opt = s508_pclk_options; break; } /* Verify IRQ configuration options */ if (!get_option_index(irq_opt, hw->irq)) { printk(KERN_INFO "%s: IRQ %d is invalid!\n", modname, hw->irq); return -EINVAL; } /* Verify CPU clock rate configuration options */ if (hw->pclk == 0) hw->pclk = pclk_opt[1]; /* use default */ else if (!get_option_index(pclk_opt, hw->pclk)) { printk(KERN_INFO "%s: CPU clock %u is invalid!\n", modname, hw->pclk); return -EINVAL; } printk(KERN_INFO "%s: assuming CPU clock rate of %u kHz.\n", modname, hw->pclk); /* Setup adapter dual-port memory window and test memory */ if (hw->dpmbase == 0) { err = sdla_autodpm(hw); if (err) { printk(KERN_INFO "%s: can't find available memory region!\n", modname); return err; } } else if (!get_option_index(dpmbase_opt, virt_to_phys(hw->dpmbase))) { printk(KERN_INFO "%s: memory address 0x%lX is invalid!\n", modname, virt_to_phys(hw->dpmbase)); return -EINVAL; } else if (sdla_setdpm(hw)) { printk(KERN_INFO
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -