⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sdladrv.c

📁 powerpc内核mpc8241linux系统下net驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************** sdladrv.c	SDLA Support Module.  Main module.**		This module is a library of common hardware-specific functions*		used by all Sangoma drivers.** Author:	Gene Kozin	<genek@compuserve.com>** Copyright:	(c) 1995-1996 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.* ============================================================================* 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/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/sched.h>	/* for jiffies, HZ, etc. */#include <linux/sdladrv.h>	/* API definitions */#include <linux/sdlasfm.h>	/* SDLA firmware module definitions */#include <linux/init.h>#include <asm/io.h>		/* for inb(), outb(), etc. */#define _INB(port)		(inb(port))#define _OUTB(port, byte)	(outb((byte),(port)))#define	SYSTEM_TICK		jiffies#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/****** Function Prototypes *************************************************//* Module entry points. These are called by the OS and must be public. */int init_module (void);void cleanup_module (void);/* 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);/* Miscellaneous functions */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);/****** Global Data ********************************************************** * Note: All data must be explicitly initialized!!! *//* private data */static char modname[]	= "sdladrv";static char fullname[]	= "SDLA Support Module";static char copyright[]	= "(c) 1995-1996 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};/******* 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 */#ifdef MODULEint init_module (void)#else__initfunc(int wanpipe_init(void))#endif{	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		return 0;}#ifdef MODULE/*============================================================================ * Module 'remove' entry point. * o release all remaining system resources */void cleanup_module (void){}#endif/******* 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;	if (sdla_detect(hw))	{		printk(KERN_ERR "%s: adapter S%04u not found at port 0x%X!\n",			modname, hw->type, hw->port)		;		return -EINVAL;	}	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_ERR "%s: IRQ %d is illegal!\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_ERR "%s: CPU clock %u is illegal!\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_ERR				"%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_ERR "%s: memory address 0x%lX is illegal!\n",			modname, virt_to_phys(hw->dpmbase))		;		return -EINVAL;	} 	else if (sdla_setdpm(hw))	{		printk(KERN_ERR			"%s: 8K memory region at 0x%lX is not available!\n",			modname, virt_to_phys(hw->dpmbase));		return -EINVAL;	} 	printk(KERN_INFO "%s: dual-port memory window is set at 0x%lX.\n",		modname, virt_to_phys(hw->dpmbase));	printk(KERN_INFO "%s: found %luK bytes of on-board memory.\n",		modname, hw->memory / 1024);	/* Load firmware. If loader fails then shut down adapter */	err = sdla_load(hw, sfm, len);	if (err) sdla_down(hw);		/* shutdown adapter */	return err;} /*============================================================================ * Shut down SDLA: disable shared memory access and interrupts, stop CPU, etc. */EXPORT_SYMBOL(sdla_down);int sdla_down (sdlahw_t* hw){	unsigned port = hw->port;	int i;	if (!port) return -EFAULT;	switch (hw->type)	{	case SDLA_S502A:		_OUTB(port, 0x08);		/* halt CPU */		_OUTB(port, 0x08);		_OUTB(port, 0x08);		hw->regs[0] = 0x08;		_OUTB(port + 1, 0xFF);		/* close memory window */		hw->regs[1] = 0xFF;		break;	case SDLA_S502E:		_OUTB(port + 3, 0);		/* stop CPU */		_OUTB(port, 0);			/* reset board */		for (i = 0; i < S502E_IORANGE; ++i)			hw->regs[i] = 0		;		break;	case SDLA_S503:	case SDLA_S507:	case SDLA_S508:		_OUTB(port, 0);			/* reset board logic */		hw->regs[0] = 0;		break;	default:		return -EINVAL;	}	return 0;}/*============================================================================ * Map shared memory window into SDLA address space. */EXPORT_SYMBOL(sdla_mapmem);int sdla_mapmem (sdlahw_t* hw, unsigned long addr){	unsigned port = hw->port;	register int tmp;	switch (hw->type)	{	case SDLA_S502A:	case SDLA_S502E:		if (addr < S502_MAXMEM)	/* verify parameter */		{			tmp = addr >> 13;	/* convert to register mask */			_OUTB(port + 2, tmp);			hw->regs[2] = tmp;		}		else return -EINVAL;		break;	case SDLA_S503:		if (addr < S503_MAXMEM)	/* verify parameter */		{			tmp = (hw->regs[0] & 0x8F) | ((addr >> 9) & 0x70);			_OUTB(port, tmp);			hw->regs[0] = tmp;		}		else return -EINVAL;		break;	case SDLA_S507:		if (addr < S507_MAXMEM)		{			if (!(_INB(port) & 0x02))				return -EIO			;			tmp = addr >> 13;	/* convert to register mask */			_OUTB(port + 2, tmp);			hw->regs[2] = tmp;		}		else return -EINVAL;		break;	case SDLA_S508:		if (addr < S508_MAXMEM)		{			tmp = addr >> 13;	/* convert to register mask */			_OUTB(port + 2, tmp);			hw->regs[2] = tmp;		}		else return -EINVAL;		break;	default:		return -EINVAL;	}	hw->vector = addr & 0xFFFFE000L;	return 0;}/*============================================================================ * Enable interrupt generation. */ EXPORT_SYMBOL(sdla_inten);int sdla_inten (sdlahw_t* hw){	unsigned port = hw->port;	int tmp, i;	switch (hw->type)	{	case SDLA_S502E:		/* Note thar interrupt control operations on S502E are allowed		 * only if CPU is enabled (bit 0 of status register is set).		 */		if (_INB(port) & 0x01)		{			_OUTB(port, 0x02);	/* bit1 = 1, bit2 = 0 */			_OUTB(port, 0x06);	/* bit1 = 1, bit2 = 1 */			hw->regs[0] = 0x06;		}		else return -EIO;		break;	case SDLA_S503:		tmp = hw->regs[0] | 0x04;		_OUTB(port, tmp);		hw->regs[0] = tmp;		/* update mirror */		for (i = 0; i < SDLA_IODELAY; ++i);	/* delay */		if (!(_INB(port) & 0x02))		/* verify */			return -EIO		;		break;	case SDLA_S508:		tmp = hw->regs[0] | 0x10;		_OUTB(port, tmp);		hw->regs[0] = tmp;		/* update mirror */		for (i = 0; i < SDLA_IODELAY; ++i);	/* delay */		if (!(_INB(port + 1) & 0x10))		/* verify */			return -EIO		;		break;	case SDLA_S502A:	case SDLA_S507:		break;	default:		return -EINVAL;	}	return 0;}/*============================================================================ * Disable interrupt generation. */EXPORT_SYMBOL(sdla_intde);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -