// SPDX-License-Identifier: GPL-2.0-only /* * linux/arch/arm/kernel/ecard.c * * Copyright 1995-2001 Russell King * * Find all installed expansion cards, and handle interrupts from them. * * Created from information from Acorns RiscOS3 PRMs * * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether * podule slot. * 06-May-1997 RMK Added blacklist for cards whose loader doesn't work. * 12-Sep-1997 RMK Created new handling of interrupt enables/disables * - cards can now register their own routine to control * interrupts (recommended). * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled * on reset from Linux. (Caused cards not to respond * under RiscOS without hard reset). * 15-Feb-1998 RMK Added DMA support * 12-Sep-1998 RMK Added EASI support * 10-Jan-1999 RMK Run loaders in a simulated RISC OS environment. * 17-Apr-1999 RMK Support for EASI Type C cycles.
*/ #define ECARD_C
/* List of descriptions of cards which don't have an extended * identification, or chunk directories containing a description.
*/ staticstruct expcard_quirklist quirklist[] __initdata = {
{ MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" },
{ MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, NULL, atomwide_3p_quirk },
};
/* ===================== Expansion card daemon ======================== */ /* * Since the loader programs on the expansion cards need to be run * in a specific environment, create a separate task with this * environment up, and pass requests to this task as and when we * need to. * * This should allow 99% of loaders to be called from Linux. * * From a security standpoint, we trust the card vendors. This * may be a misplaced trust.
*/ staticvoid ecard_task_reset(struct ecard_request *req)
{ struct expansion_card *ec = req->ec; struct resource *res;
/* * The card maintains an index which increments the address * into a 4096-byte page on each access. We need to keep * track of the counter.
*/ staticunsignedint index; unsignedint page;
/* * If we are reading offset 0, or our current index is * greater than the offset, reset the hardware index counter.
*/ if (off == 0 || index > off) {
writeb(0, base);
index = 0;
}
/* * Increment the hardware index counter until we get to the * required offset. The read bytes are discarded.
*/ while (index < off) {
readb(base + page);
index += 1;
}
while (len--) {
*buf++ = readb(base + page);
index += 1;
}
} else { unsignedlong base = (ec->easi
? &ec->resource[ECARD_RES_EASI]
: &ec->resource[ECARD_RES_IOCSYNC])->start; void __iomem *pbase = (void __iomem *)base;
if (!req->use_loader || !ec->loader) {
off *= 4; while (len--) {
*buf++ = readb(pbase + off);
off += 4;
}
} else { while(len--) { /* * The following is required by some * expansion card loader programs.
*/
*(unsignedlong *)0x108 = 0;
*buf++ = ecard_loader_read(off++, base,
ec->loader);
}
}
}
/* * Set up the expansion card daemon's page tables.
*/ staticvoid ecard_init_pgtables(struct mm_struct *mm)
{ struct vm_area_struct vma = TLB_FLUSH_VMA(mm, VM_EXEC);
/* We want to set up the page tables for the following mapping: * Virtual Physical * 0x03000000 0x03000000 * 0x03010000 unmapped * 0x03210000 0x03210000 * 0x03400000 unmapped * 0x08000000 0x08000000 * 0x10000000 unmapped * * FIXME: we don't follow this 100% yet.
*/
pgd_t *src_pgd, *dst_pgd;
staticint
ecard_task(void * unused)
{ /* * Allocate a mm. We're not a lazy-TLB kernel task since we need * to set page table entries where the user space would be. Note * that this also creates the page tables. Failure is not an * option here.
*/ if (ecard_init_mm())
panic("kecardd: unable to alloc mm\n");
/* * Wake the expansion card daemon to action our request. * * FIXME: The test here is not sufficient to detect if the * kcardd is running.
*/ staticvoid ecard_call(struct ecard_request *req)
{
DECLARE_COMPLETION_ONSTACK(completion);
/* * Enable and disable interrupts from expansion cards. * (interrupts are disabled for these functions). * * They are not meant to be called directly, but via enable/disable_irq.
*/ staticvoid ecard_irq_unmask(struct irq_data *d)
{
ecard_t *ec = irq_data_get_irq_chip_data(d);
if (ec) { if (!ec->ops)
ec->ops = &ecard_default_ops;
if (ec->claimed && ec->ops->irqenable)
ec->ops->irqenable(ec, d->irq); else
printk(KERN_ERR "ecard: rejecting request to " "enable IRQs for %d\n", d->irq);
}
}
/* * If the timer interrupt has not run since the last million * unrecognised expansion card interrupts, then there is * something seriously wrong. Disable the expansion card * interrupts so at least we can continue. * * Maybe we ought to start a timer to re-enable them some time * later?
*/ if (last == jiffies) {
lockup += 1; if (lockup > 1000000) {
printk(KERN_ERR "\nInterrupt lockup detected - " "disabling all expansion card interrupts\n");
/* * If we did not recognise the source of this interrupt, * warn the user, but don't flood the user with these messages.
*/ if (!last || time_after(jiffies, last + 5*HZ)) {
last = jiffies;
printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
ecard_dump_irq_state();
}
}
staticvoid ecard_irq_handler(struct irq_desc *desc)
{
ecard_t *ec; int called = 0;
desc->irq_data.chip->irq_mask(&desc->irq_data); for (ec = cards; ec; ec = ec->next) { int pending;
if (!ec->claimed || !ec->irq || ec->slot_no == 8) continue;
int ecard_request_resources(struct expansion_card *ec)
{ int i, err = 0;
for (i = 0; i < ECARD_NUM_RESOURCES; i++) { if (ecard_resource_end(ec, i) &&
!request_mem_region(ecard_resource_start(ec, i),
ecard_resource_len(ec, i),
ec->dev.driver->name)) {
err = -EBUSY; break;
}
}
if (err) { while (i--) if (ecard_resource_end(ec, i))
release_mem_region(ecard_resource_start(ec, i),
ecard_resource_len(ec, i));
} return err;
}
EXPORT_SYMBOL(ecard_request_resources);
void ecard_release_resources(struct expansion_card *ec)
{ int i;
for (i = 0; i < ECARD_NUM_RESOURCES; i++) if (ecard_resource_end(ec, i))
release_mem_region(ecard_resource_start(ec, i),
ecard_resource_len(ec, i));
}
EXPORT_SYMBOL(ecard_release_resources);
/* Disable interrupts on each port */ for (i = 0x2000; i <= 0x2800; i += 0x0400)
writeb(0, addr + i + 4);
}
/* * Probe for an expansion card. * * If bit 1 of the first byte of the card is set, then the * card does not exist.
*/ staticint __init ecard_probe(int slot, unsigned irq, card_type_t type)
{
ecard_t **ecp;
ecard_t *ec; struct ex_ecid cid; void __iomem *addr; int i, rc;
for (i = 0; i < ARRAY_SIZE(quirklist); i++) if (quirklist[i].manufacturer == ec->cid.manufacturer &&
quirklist[i].product == ec->cid.product) { if (quirklist[i].type)
ec->card_desc = quirklist[i].type; if (quirklist[i].init)
quirklist[i].init(ec); break;
}
ec->irq = irq;
/* * hook the interrupt handlers
*/ if (slot < 8) {
irq_set_chip_and_handler(ec->irq, &ecard_chip,
handle_level_irq);
irq_set_chip_data(ec->irq, ec);
irq_clear_status_flags(ec->irq, IRQ_NOREQUEST);
}
#ifdef CONFIG_ARCH_RPC /* On RiscPC, only first two slots have DMA capability */ if (slot < 2)
ec->dma = 2 + slot; #endif
for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
*ecp = ec;
slot_to_expcard[slot] = ec;
rc = device_register(&ec->dev); if (rc) goto nodev;
return 0;
nodev:
ecard_free_card(ec);
nomem: return rc;
}
/* * Initialise the expansion card system. * Locate all hardware - interrupt management and * actual cards.
*/ staticint __init ecard_init(void)
{ struct task_struct *task; int slot, irqbase;
/* * Restore the default operations. We ensure that the * ops are set before we change the data.
*/
ec->ops = &ecard_default_ops;
barrier();
ec->irq_data = NULL;
}
/* * Before rebooting, we must make sure that the expansion card is in a * sensible state, so it can be re-detected. This means that the first * page of the ROM must be visible. We call the expansion cards reset * handler, if any.
*/ staticvoid ecard_drv_shutdown(struct device *dev)
{ struct expansion_card *ec = ECARD_DEV(dev); struct ecard_driver *drv = ECARD_DRV(dev->driver); struct ecard_request req;
if (dev->driver) { if (drv->shutdown)
drv->shutdown(ec);
ec->claimed = 0;
}
/* * If this card has a loader, call the reset handler.
*/ if (ec->loader) {
req.fn = ecard_task_reset;
req.ec = ec;
ecard_call(&req);
}
}
int ecard_register_driver(struct ecard_driver *drv)
{
drv->drv.bus = &ecard_bus_type;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.