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

📄 m8260_pci_erratum9.c

📁 linux-2.6.15.6
💻 C
字号:
/* * arch/ppc/platforms/mpc8260_pci9.c * * Workaround for device erratum PCI 9. * See Motorola's "XPC826xA Family Device Errata Reference." * The erratum applies to all 8260 family Hip4 processors.  It is scheduled  * to be fixed in HiP4 Rev C.  Erratum PCI 9 states that a simultaneous PCI  * inbound write transaction and PCI outbound read transaction can result in a  * bus deadlock.  The suggested workaround is to use the IDMA controller to  * perform all reads from PCI configuration, memory, and I/O space. * * Author:  andy_lowe@mvista.com * * 2003 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/kernel.h>#include <linux/config.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/types.h>#include <linux/string.h>#include <asm/io.h>#include <asm/pci-bridge.h>#include <asm/machdep.h>#include <asm/byteorder.h>#include <asm/mpc8260.h>#include <asm/immap_cpm2.h>#include <asm/cpm2.h>#include "m82xx_pci.h"#ifdef CONFIG_8260_PCI9/*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */#define IDMA_XFER_BUF_SIZE 64	/* size of the IDMA transfer buffer *//* define a structure for the IDMA dpram usage */typedef struct idma_dpram_s {	idma_t pram;				/* IDMA parameter RAM */	u_char xfer_buf[IDMA_XFER_BUF_SIZE];	/* IDMA transfer buffer */	idma_bd_t bd;				/* buffer descriptor */} idma_dpram_t;/* define offsets relative to start of IDMA dpram */#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t))#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE)/* define globals */static volatile idma_dpram_t *idma_dpram;/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined,  * where n is 1, 2, 3, or 4.  This selects the IDMA channel used for  * the PCI9 workaround. */#ifdef CONFIG_8260_PCI9_IDMA1#define IDMA_CHAN 0#define PROFF_IDMA PROFF_IDMA1_BASE#define IDMA_PAGE CPM_CR_IDMA1_PAGE#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK#endif#ifdef CONFIG_8260_PCI9_IDMA2#define IDMA_CHAN 1#define PROFF_IDMA PROFF_IDMA2_BASE#define IDMA_PAGE CPM_CR_IDMA2_PAGE#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK#endif#ifdef CONFIG_8260_PCI9_IDMA3#define IDMA_CHAN 2#define PROFF_IDMA PROFF_IDMA3_BASE#define IDMA_PAGE CPM_CR_IDMA3_PAGE#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK#endif#ifdef CONFIG_8260_PCI9_IDMA4#define IDMA_CHAN 3#define PROFF_IDMA PROFF_IDMA4_BASE#define IDMA_PAGE CPM_CR_IDMA4_PAGE#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK#endifvoid idma_pci9_init(void){	uint dpram_offset;	volatile idma_t *pram;	volatile im_idma_t *idma_reg;	volatile cpm2_map_t *immap = cpm2_immr;	/* allocate IDMA dpram */	dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64);	idma_dpram = cpm_dpram_addr(dpram_offset); 	/* initialize the IDMA parameter RAM */	memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));	pram = &idma_dpram->pram;	pram->ibase = dpram_offset + IDMA_BD_OFFSET;	pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET;	pram->ss_max = 32;	pram->dts = 32;	/* initialize the IDMA_BASE pointer to the IDMA parameter RAM */	*((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset;	/* initialize the IDMA registers */	idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1;	idma_reg[IDMA_CHAN].idmr = 0;		/* mask all IDMA interrupts */	idma_reg[IDMA_CHAN].idsr = 0xff;	/* clear all event flags */	printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n",		IDMA_CHAN + 1);	return;}/* Use the IDMA controller to transfer data from I/O memory to local RAM. * The src address must be a physical address suitable for use by the DMA  * controller with no translation.  The dst address must be a kernel virtual  * address.  The dst address is translated to a physical address via  * virt_to_phys(). * The sinc argument specifies whether or not the source address is incremented * by the DMA controller.  The source address is incremented if and only if sinc * is non-zero.  The destination address is always incremented since the  * destination is always host RAM. */static void idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc){	unsigned long flags;	volatile idma_t *pram = &idma_dpram->pram;	volatile idma_bd_t *bd = &idma_dpram->bd;	volatile cpm2_map_t *immap = cpm2_immr;	local_irq_save(flags);	/* initialize IDMA parameter RAM for this transfer */	if (sinc)		pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC			  | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;	else		pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC 			  | IDMA_DCM_SD_MEM2MEM;	pram->ibdptr = pram->ibase;	pram->sts = unit_size;	pram->istate = 0;	/* initialize the buffer descriptor */	bd->dst = virt_to_phys(dst);	bd->src = (uint) src;	bd->len = bytes;	bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL		  | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;	/* issue the START_IDMA command to the CP */	while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);	immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,					 CPM_CR_START_IDMA) | CPM_CR_FLG;	while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);	/* wait for transfer to complete */	while(bd->flags & IDMA_BD_V);	local_irq_restore(flags);	return;}/* Use the IDMA controller to transfer data from I/O memory to local RAM. * The dst address must be a physical address suitable for use by the DMA  * controller with no translation.  The src address must be a kernel virtual  * address.  The src address is translated to a physical address via  * virt_to_phys(). * The dinc argument specifies whether or not the dest address is incremented * by the DMA controller.  The source address is incremented if and only if sinc * is non-zero.  The source address is always incremented since the  * source is always host RAM. */static void idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc){	unsigned long flags;	volatile idma_t *pram = &idma_dpram->pram;	volatile idma_bd_t *bd = &idma_dpram->bd;	volatile cpm2_map_t *immap = cpm2_immr;	local_irq_save(flags);	/* initialize IDMA parameter RAM for this transfer */	if (dinc)		pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC			  | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;	else		pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC 			  | IDMA_DCM_SD_MEM2MEM;	pram->ibdptr = pram->ibase;	pram->sts = unit_size;	pram->istate = 0;	/* initialize the buffer descriptor */	bd->dst = (uint) dst;	bd->src = virt_to_phys(src);	bd->len = bytes;	bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL		  | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;	/* issue the START_IDMA command to the CP */	while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);	immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,					 CPM_CR_START_IDMA) | CPM_CR_FLG;	while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);	/* wait for transfer to complete */	while(bd->flags & IDMA_BD_V);	local_irq_restore(flags);	return;}/* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if * the unit_size is 4. */static void idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc){	int i;	u8 *p;	idma_pci9_read(dst, src, bytes, unit_size, sinc);	switch(unit_size) {		case 2:			for (i = 0, p = dst; i < bytes; i += 2, p += 2)				swab16s((u16 *) p);			break;		case 4:			for (i = 0, p = dst; i < bytes; i += 4, p += 4)				swab32s((u32 *) p);			break;		default:			break;	}}EXPORT_SYMBOL(idma_pci9_init);EXPORT_SYMBOL(idma_pci9_read);EXPORT_SYMBOL(idma_pci9_read_le);static inline int is_pci_mem(unsigned long addr){	if (addr >= M82xx_PCI_LOWER_MMIO &&		addr <= M82xx_PCI_UPPER_MMIO)		return 1;	if (addr >= M82xx_PCI_LOWER_MEM &&		addr <= M82xx_PCI_UPPER_MEM)		return 1;	return 0;}#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000))int readb(volatile unsigned char *addr){	u8 val;	unsigned long pa = iopa((unsigned long) addr);	if (!is_pci_mem(pa))		return in_8(addr);	idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);	return val;}int readw(volatile unsigned short *addr){	u16 val;	unsigned long pa = iopa((unsigned long) addr);	if (!is_pci_mem(pa))		return in_le16(addr);	idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);	return swab16(val);}unsigned readl(volatile unsigned *addr){	u32 val;	unsigned long pa = iopa((unsigned long) addr);	if (!is_pci_mem(pa))		return in_le32(addr);	idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);	return swab32(val);}int inb(unsigned port){	u8 val;	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);	return val;}int inw(unsigned port){	u16 val;	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);	return swab16(val);}unsigned inl(unsigned port){	u32 val;	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);	return swab32(val);}void insb(unsigned port, void *buf, int ns){	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0);}void insw(unsigned port, void *buf, int ns){	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);}void insl(unsigned port, void *buf, int nl){	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);}void insw_ns(unsigned port, void *buf, int ns){	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);}void insl_ns(unsigned port, void *buf, int nl){	u8 *addr = (u8 *)(port + _IO_BASE);	idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);}void *memcpy_fromio(void *dest, unsigned long src, size_t count){	unsigned long pa = iopa((unsigned long) src);	if (is_pci_mem(pa))		idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1);	else		memcpy(dest, (void *)src, count);	return dest;}EXPORT_SYMBOL(readb);EXPORT_SYMBOL(readw);EXPORT_SYMBOL(readl);EXPORT_SYMBOL(inb);EXPORT_SYMBOL(inw);EXPORT_SYMBOL(inl);EXPORT_SYMBOL(insb);EXPORT_SYMBOL(insw);EXPORT_SYMBOL(insl);EXPORT_SYMBOL(insw_ns);EXPORT_SYMBOL(insl_ns);EXPORT_SYMBOL(memcpy_fromio);#endif	/* ifdef CONFIG_8260_PCI9 *//* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c. * Copyright (C) 1998 Gabriel Paubert. */#ifndef CONFIG_8260_PCI9#define cfg_read(val, addr, type, op)	*val = op((type)(addr))#else#define cfg_read(val, addr, type, op) \	idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0)#endif#define cfg_write(val, addr, type, op)	op((type *)(addr), (val))static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where,			 int size, u32 value){	struct pci_controller *hose = pbus->sysdata;	u8 cfg_type = 0;	if (ppc_md.pci_exclude_device)		if (ppc_md.pci_exclude_device(pbus->number, devfn))			return PCIBIOS_DEVICE_NOT_FOUND;	if (hose->set_cfg_type)		if (pbus->number != hose->first_busno)			cfg_type = 1;	out_be32(hose->cfg_addr,		 (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)		 | ((pbus->number - hose->bus_offset) << 8) | 0x80);	switch (size)	{		case 1:			cfg_write(value, hose->cfg_data + (where & 3), u8, out_8);			break;		case 2:			cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16);			break;		case 4:			cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32);			break;	}			return PCIBIOS_SUCCESSFUL;}static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where,			 int size, u32 *value){	struct pci_controller *hose = pbus->sysdata;	u8 cfg_type = 0;	if (ppc_md.pci_exclude_device)		if (ppc_md.pci_exclude_device(pbus->number, devfn))			return PCIBIOS_DEVICE_NOT_FOUND;	if (hose->set_cfg_type)		if (pbus->number != hose->first_busno)			cfg_type = 1;	out_be32(hose->cfg_addr,		 (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)		 | ((pbus->number - hose->bus_offset) << 8) | 0x80);	switch (size)	{		case 1:			cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8);			break;		case 2:			cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16);			break;		case 4:			cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32);			break;	}			return PCIBIOS_SUCCESSFUL;}static struct pci_ops indirect_pci_ops ={	.read = indirect_read_config,	.write = indirect_write_config,};voidsetup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data){	hose->ops = &indirect_pci_ops;	hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4);	hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4);}

⌨️ 快捷键说明

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