ebus.c

来自「linux 内核源代码」· C语言 代码 · 共 550 行

C
550
字号
/* $Id: ebus.c,v 1.64 2001/11/08 04:41:33 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be) * Copyright (C) 1999  David S. Miller (davem@redhat.com) */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/pci.h>#include <asm/system.h>#include <asm/page.h>#include <asm/ebus.h>#include <asm/oplib.h>#include <asm/prom.h>#include <asm/of_device.h>#include <asm/bpp.h>#include <asm/irq.h>#include <asm/io.h>/* EBUS dma library. */#define EBDMA_CSR	0x00UL	/* Control/Status */#define EBDMA_ADDR	0x04UL	/* DMA Address */#define EBDMA_COUNT	0x08UL	/* DMA Count */#define EBDMA_CSR_INT_PEND	0x00000001#define EBDMA_CSR_ERR_PEND	0x00000002#define EBDMA_CSR_DRAIN		0x00000004#define EBDMA_CSR_INT_EN	0x00000010#define EBDMA_CSR_RESET		0x00000080#define EBDMA_CSR_WRITE		0x00000100#define EBDMA_CSR_EN_DMA	0x00000200#define EBDMA_CSR_CYC_PEND	0x00000400#define EBDMA_CSR_DIAG_RD_DONE	0x00000800#define EBDMA_CSR_DIAG_WR_DONE	0x00001000#define EBDMA_CSR_EN_CNT	0x00002000#define EBDMA_CSR_TC		0x00004000#define EBDMA_CSR_DIS_CSR_DRN	0x00010000#define EBDMA_CSR_BURST_SZ_MASK	0x000c0000#define EBDMA_CSR_BURST_SZ_1	0x00080000#define EBDMA_CSR_BURST_SZ_4	0x00000000#define EBDMA_CSR_BURST_SZ_8	0x00040000#define EBDMA_CSR_BURST_SZ_16	0x000c0000#define EBDMA_CSR_DIAG_EN	0x00100000#define EBDMA_CSR_DIS_ERR_PEND	0x00400000#define EBDMA_CSR_TCI_DIS	0x00800000#define EBDMA_CSR_EN_NEXT	0x01000000#define EBDMA_CSR_DMA_ON	0x02000000#define EBDMA_CSR_A_LOADED	0x04000000#define EBDMA_CSR_NA_LOADED	0x08000000#define EBDMA_CSR_DEV_ID_MASK	0xf0000000#define EBUS_DMA_RESET_TIMEOUT	10000static void __ebus_dma_reset(struct ebus_dma_info *p, int no_drain){	int i;	u32 val = 0;	writel(EBDMA_CSR_RESET, p->regs + EBDMA_CSR);	udelay(1);	if (no_drain)		return;	for (i = EBUS_DMA_RESET_TIMEOUT; i > 0; i--) {		val = readl(p->regs + EBDMA_CSR);		if (!(val & (EBDMA_CSR_DRAIN | EBDMA_CSR_CYC_PEND)))			break;		udelay(10);	}}static irqreturn_t ebus_dma_irq(int irq, void *dev_id){	struct ebus_dma_info *p = dev_id;	unsigned long flags;	u32 csr = 0;	spin_lock_irqsave(&p->lock, flags);	csr = readl(p->regs + EBDMA_CSR);	writel(csr, p->regs + EBDMA_CSR);	spin_unlock_irqrestore(&p->lock, flags);	if (csr & EBDMA_CSR_ERR_PEND) {		printk(KERN_CRIT "ebus_dma(%s): DMA error!\n", p->name);		p->callback(p, EBUS_DMA_EVENT_ERROR, p->client_cookie);		return IRQ_HANDLED;	} else if (csr & EBDMA_CSR_INT_PEND) {		p->callback(p,			    (csr & EBDMA_CSR_TC) ?			    EBUS_DMA_EVENT_DMA : EBUS_DMA_EVENT_DEVICE,			    p->client_cookie);		return IRQ_HANDLED;	}	return IRQ_NONE;}int ebus_dma_register(struct ebus_dma_info *p){	u32 csr;	if (!p->regs)		return -EINVAL;	if (p->flags & ~(EBUS_DMA_FLAG_USE_EBDMA_HANDLER |			 EBUS_DMA_FLAG_TCI_DISABLE))		return -EINVAL;	if ((p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) && !p->callback)		return -EINVAL;	if (!strlen(p->name))		return -EINVAL;	__ebus_dma_reset(p, 1);	csr = EBDMA_CSR_BURST_SZ_16 | EBDMA_CSR_EN_CNT;	if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)		csr |= EBDMA_CSR_TCI_DIS;	writel(csr, p->regs + EBDMA_CSR);	return 0;}EXPORT_SYMBOL(ebus_dma_register);int ebus_dma_irq_enable(struct ebus_dma_info *p, int on){	unsigned long flags;	u32 csr;	if (on) {		if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {			if (request_irq(p->irq, ebus_dma_irq, IRQF_SHARED, p->name, p))				return -EBUSY;		}		spin_lock_irqsave(&p->lock, flags);		csr = readl(p->regs + EBDMA_CSR);		csr |= EBDMA_CSR_INT_EN;		writel(csr, p->regs + EBDMA_CSR);		spin_unlock_irqrestore(&p->lock, flags);	} else {		spin_lock_irqsave(&p->lock, flags);		csr = readl(p->regs + EBDMA_CSR);		csr &= ~EBDMA_CSR_INT_EN;		writel(csr, p->regs + EBDMA_CSR);		spin_unlock_irqrestore(&p->lock, flags);		if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {			free_irq(p->irq, p);		}	}	return 0;}EXPORT_SYMBOL(ebus_dma_irq_enable);void ebus_dma_unregister(struct ebus_dma_info *p){	unsigned long flags;	u32 csr;	int irq_on = 0;	spin_lock_irqsave(&p->lock, flags);	csr = readl(p->regs + EBDMA_CSR);	if (csr & EBDMA_CSR_INT_EN) {		csr &= ~EBDMA_CSR_INT_EN;		writel(csr, p->regs + EBDMA_CSR);		irq_on = 1;	}	spin_unlock_irqrestore(&p->lock, flags);	if (irq_on)		free_irq(p->irq, p);}EXPORT_SYMBOL(ebus_dma_unregister);int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, size_t len){	unsigned long flags;	u32 csr;	int err;	if (len >= (1 << 24))		return -EINVAL;	spin_lock_irqsave(&p->lock, flags);	csr = readl(p->regs + EBDMA_CSR);	err = -EINVAL;	if (!(csr & EBDMA_CSR_EN_DMA))		goto out;	err = -EBUSY;	if (csr & EBDMA_CSR_NA_LOADED)		goto out;	writel(len,      p->regs + EBDMA_COUNT);	writel(bus_addr, p->regs + EBDMA_ADDR);	err = 0;out:	spin_unlock_irqrestore(&p->lock, flags);	return err;}EXPORT_SYMBOL(ebus_dma_request);void ebus_dma_prepare(struct ebus_dma_info *p, int write){	unsigned long flags;	u32 csr;	spin_lock_irqsave(&p->lock, flags);	__ebus_dma_reset(p, 0);	csr = (EBDMA_CSR_INT_EN |	       EBDMA_CSR_EN_CNT |	       EBDMA_CSR_BURST_SZ_16 |	       EBDMA_CSR_EN_NEXT);	if (write)		csr |= EBDMA_CSR_WRITE;	if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)		csr |= EBDMA_CSR_TCI_DIS;	writel(csr, p->regs + EBDMA_CSR);	spin_unlock_irqrestore(&p->lock, flags);}EXPORT_SYMBOL(ebus_dma_prepare);unsigned int ebus_dma_residue(struct ebus_dma_info *p){	return readl(p->regs + EBDMA_COUNT);}EXPORT_SYMBOL(ebus_dma_residue);unsigned int ebus_dma_addr(struct ebus_dma_info *p){	return readl(p->regs + EBDMA_ADDR);}EXPORT_SYMBOL(ebus_dma_addr);void ebus_dma_enable(struct ebus_dma_info *p, int on){	unsigned long flags;	u32 orig_csr, csr;	spin_lock_irqsave(&p->lock, flags);	orig_csr = csr = readl(p->regs + EBDMA_CSR);	if (on)		csr |= EBDMA_CSR_EN_DMA;	else		csr &= ~EBDMA_CSR_EN_DMA;	if ((orig_csr & EBDMA_CSR_EN_DMA) !=	    (csr & EBDMA_CSR_EN_DMA))		writel(csr, p->regs + EBDMA_CSR);	spin_unlock_irqrestore(&p->lock, flags);}EXPORT_SYMBOL(ebus_dma_enable);struct linux_ebus *ebus_chain = NULL;static inline void *ebus_alloc(size_t size){	void *mem;	mem = kzalloc(size, GFP_ATOMIC);	if (!mem)		panic("ebus_alloc: out of memory");	return mem;}static void __init fill_ebus_child(struct device_node *dp,				   struct linux_ebus_child *dev,				   int non_standard_regs){	struct of_device *op;	const int *regs;	int i, len;	dev->prom_node = dp;	printk(" (%s)", dp->name);	regs = of_get_property(dp, "reg", &len);	if (!regs)		dev->num_addrs = 0;	else		dev->num_addrs = len / sizeof(regs[0]);	if (non_standard_regs) {		/* This is to handle reg properties which are not		 * in the parent relative format.  One example are		 * children of the i2c device on CompactPCI systems.		 *		 * So, for such devices we just record the property		 * raw in the child resources.		 */		for (i = 0; i < dev->num_addrs; i++)			dev->resource[i].start = regs[i];	} else {		for (i = 0; i < dev->num_addrs; i++) {			int rnum = regs[i];			if (rnum >= dev->parent->num_addrs) {				prom_printf("UGH: property for %s was %d, need < %d\n",					    dp->name, len, dev->parent->num_addrs);				prom_halt();			}			dev->resource[i].start = dev->parent->resource[i].start;			dev->resource[i].end = dev->parent->resource[i].end;			dev->resource[i].flags = IORESOURCE_MEM;			dev->resource[i].name = dp->name;		}	}	op = of_find_device_by_node(dp);	if (!op) {		dev->num_irqs = 0;	} else {		dev->num_irqs = op->num_irqs;		for (i = 0; i < dev->num_irqs; i++)			dev->irqs[i] = op->irqs[i];	}	if (!dev->num_irqs) {		/*		 * Oh, well, some PROMs don't export interrupts		 * property to children of EBus devices...		 *		 * Be smart about PS/2 keyboard and mouse.		 */		if (!strcmp(dev->parent->prom_node->name, "8042")) {			if (!strcmp(dev->prom_node->name, "kb_ps2")) {				dev->num_irqs = 1;				dev->irqs[0] = dev->parent->irqs[0];			} else {				dev->num_irqs = 1;				dev->irqs[0] = dev->parent->irqs[1];			}		}	}}static int __init child_regs_nonstandard(struct linux_ebus_device *dev){	if (!strcmp(dev->prom_node->name, "i2c") ||	    !strcmp(dev->prom_node->name, "SUNW,lombus"))		return 1;	return 0;}static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev){	struct linux_ebus_child *child;	struct dev_archdata *sd;	struct of_device *op;	int i, len;	dev->prom_node = dp;	printk(" [%s", dp->name);	op = of_find_device_by_node(dp);	if (!op) {		dev->num_addrs = 0;		dev->num_irqs = 0;	} else {		const int *regs = of_get_property(dp, "reg", &len);		if (!regs)			len = 0;		dev->num_addrs = len / sizeof(struct linux_prom_registers);		for (i = 0; i < dev->num_addrs; i++)			memcpy(&dev->resource[i],			       &op->resource[i],			       sizeof(struct resource));		dev->num_irqs = op->num_irqs;		for (i = 0; i < dev->num_irqs; i++)			dev->irqs[i] = op->irqs[i];	}	sd = &dev->ofdev.dev.archdata;	sd->prom_node = dp;	sd->op = &dev->ofdev;	sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu;	sd->stc = dev->bus->ofdev.dev.parent->archdata.stc;	dev->ofdev.node = dp;	dev->ofdev.dev.parent = &dev->bus->ofdev.dev;	dev->ofdev.dev.bus = &ebus_bus_type;	sprintf(dev->ofdev.dev.bus_id, "ebus[%08x]", dp->node);	/* Register with core */	if (of_device_register(&dev->ofdev) != 0)		printk(KERN_DEBUG "ebus: device registration error for %s!\n",		       dp->path_component_name);	dp = dp->child;	if (dp) {		printk(" ->");		dev->children = ebus_alloc(sizeof(struct linux_ebus_child));		child = dev->children;		child->next = NULL;		child->parent = dev;		child->bus = dev->bus;		fill_ebus_child(dp, child,				child_regs_nonstandard(dev));		while ((dp = dp->sibling) != NULL) {			child->next = ebus_alloc(sizeof(struct linux_ebus_child));			child = child->next;			child->next = NULL;			child->parent = dev;			child->bus = dev->bus;			fill_ebus_child(dp, child,					child_regs_nonstandard(dev));		}	}	printk("]");}static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p){	struct pci_dev *pdev = start;	while ((pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev)))		if (pdev->device == PCI_DEVICE_ID_SUN_EBUS ||			pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS)			break;	*is_rio_p = !!(pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS));	return pdev;}void __init ebus_init(void){	struct linux_ebus_device *dev;	struct linux_ebus *ebus;	struct pci_dev *pdev;	struct device_node *dp;	int is_rio;	int num_ebus = 0;	pdev = find_next_ebus(NULL, &is_rio);	if (!pdev) {		printk("ebus: No EBus's found.\n");		return;	}	dp = pci_device_to_OF_node(pdev);	ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));	ebus->next = NULL;	ebus->is_rio = is_rio;	while (dp) {		struct device_node *child;		/* SUNW,pci-qfe uses four empty ebuses on it.		   I think we should not consider them here,		   as they have half of the properties this		   code expects and once we do PCI hot-plug,		   we'd have to tweak with the ebus_chain		   in the runtime after initialization. -jj */		if (!dp->child) {			pdev = find_next_ebus(pdev, &is_rio);			if (!pdev) {				if (ebus == ebus_chain) {					ebus_chain = NULL;					printk("ebus: No EBus's found.\n");					return;				}				break;			}			ebus->is_rio = is_rio;			dp = pci_device_to_OF_node(pdev);			continue;		}		printk("ebus%d:", num_ebus);		ebus->index = num_ebus;		ebus->prom_node = dp;		ebus->self = pdev;		ebus->ofdev.node = dp;		ebus->ofdev.dev.parent = &pdev->dev;		ebus->ofdev.dev.bus = &ebus_bus_type;		sprintf(ebus->ofdev.dev.bus_id, "ebus%d", num_ebus);		/* Register with core */		if (of_device_register(&ebus->ofdev) != 0)			printk(KERN_DEBUG "ebus: device registration error for %s!\n",			       dp->path_component_name);		child = dp->child;		if (!child)			goto next_ebus;		ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device));		dev = ebus->devices;		dev->next = NULL;		dev->children = NULL;		dev->bus = ebus;		fill_ebus_device(child, dev);		while ((child = child->sibling) != NULL) {			dev->next = ebus_alloc(sizeof(struct linux_ebus_device));			dev = dev->next;			dev->next = NULL;			dev->children = NULL;			dev->bus = ebus;			fill_ebus_device(child, dev);		}	next_ebus:		printk("\n");		pdev = find_next_ebus(pdev, &is_rio);		if (!pdev)			break;		dp = pci_device_to_OF_node(pdev);		ebus->next = ebus_alloc(sizeof(struct linux_ebus));		ebus = ebus->next;		ebus->next = NULL;		ebus->is_rio = is_rio;		++num_ebus;	}	pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */}

⌨️ 快捷键说明

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