123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*
- * ibm_ocp_mal.c
- *
- * Armin Kuster akuster@mvista.com
- * Juen, 2002
- *
- * Copyright 2002 MontaVista Softare 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.
- */
- #include <linux/config.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/netdevice.h>
- #include <linux/init.h>
- #include <linux/dma-mapping.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <asm/ocp.h>
- #include "ibm_emac_mal.h"
- // Locking: Should we share a lock with the client ? The client could provide
- // a lock pointer (optionally) in the commac structure... I don't think this is
- // really necessary though
- /* This lock protects the commac list. On today UP implementations, it's
- * really only used as IRQ protection in mal_{register,unregister}_commac()
- */
- static DEFINE_RWLOCK(mal_list_lock);
- int mal_register_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac)
- {
- unsigned long flags;
- write_lock_irqsave(&mal_list_lock, flags);
- /* Don't let multiple commacs claim the same channel */
- if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
- (mal->rx_chan_mask & commac->rx_chan_mask)) {
- write_unlock_irqrestore(&mal_list_lock, flags);
- return -EBUSY;
- }
- mal->tx_chan_mask |= commac->tx_chan_mask;
- mal->rx_chan_mask |= commac->rx_chan_mask;
- list_add(&commac->list, &mal->commac);
- write_unlock_irqrestore(&mal_list_lock, flags);
- return 0;
- }
- int mal_unregister_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac)
- {
- unsigned long flags;
- write_lock_irqsave(&mal_list_lock, flags);
- mal->tx_chan_mask &= ~commac->tx_chan_mask;
- mal->rx_chan_mask &= ~commac->rx_chan_mask;
- list_del_init(&commac->list);
- write_unlock_irqrestore(&mal_list_lock, flags);
- return 0;
- }
- int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, unsigned long size)
- {
- switch (channel) {
- case 0:
- set_mal_dcrn(mal, DCRN_MALRCBS0, size);
- break;
- #ifdef DCRN_MALRCBS1
- case 1:
- set_mal_dcrn(mal, DCRN_MALRCBS1, size);
- break;
- #endif
- #ifdef DCRN_MALRCBS2
- case 2:
- set_mal_dcrn(mal, DCRN_MALRCBS2, size);
- break;
- #endif
- #ifdef DCRN_MALRCBS3
- case 3:
- set_mal_dcrn(mal, DCRN_MALRCBS3, size);
- break;
- #endif
- default:
- return -EINVAL;
- }
- return 0;
- }
- static irqreturn_t mal_serr(int irq, void *dev_instance, struct pt_regs *regs)
- {
- struct ibm_ocp_mal *mal = dev_instance;
- unsigned long mal_error;
- /*
- * This SERR applies to one of the devices on the MAL, here we charge
- * it against the first EMAC registered for the MAL.
- */
- mal_error = get_mal_dcrn(mal, DCRN_MALESR);
- printk(KERN_ERR "%s: System Error (MALESR=%lx)\n",
- "MAL" /* FIXME: get the name right */ , mal_error);
- /* FIXME: decipher error */
- /* DIXME: distribute to commacs, if possible */
- /* Clear the error status register */
- set_mal_dcrn(mal, DCRN_MALESR, mal_error);
- return IRQ_HANDLED;
- }
- static irqreturn_t mal_txeob(int irq, void *dev_instance, struct pt_regs *regs)
- {
- struct ibm_ocp_mal *mal = dev_instance;
- struct list_head *l;
- unsigned long isr;
- isr = get_mal_dcrn(mal, DCRN_MALTXEOBISR);
- set_mal_dcrn(mal, DCRN_MALTXEOBISR, isr);
- read_lock(&mal_list_lock);
- list_for_each(l, &mal->commac) {
- struct mal_commac *mc = list_entry(l, struct mal_commac, list);
- if (isr & mc->tx_chan_mask) {
- mc->ops->txeob(mc->dev, isr & mc->tx_chan_mask);
- }
- }
- read_unlock(&mal_list_lock);
- return IRQ_HANDLED;
- }
- static irqreturn_t mal_rxeob(int irq, void *dev_instance, struct pt_regs *regs)
- {
- struct ibm_ocp_mal *mal = dev_instance;
- struct list_head *l;
- unsigned long isr;
- isr = get_mal_dcrn(mal, DCRN_MALRXEOBISR);
- set_mal_dcrn(mal, DCRN_MALRXEOBISR, isr);
- read_lock(&mal_list_lock);
- list_for_each(l, &mal->commac) {
- struct mal_commac *mc = list_entry(l, struct mal_commac, list);
- if (isr & mc->rx_chan_mask) {
- mc->ops->rxeob(mc->dev, isr & mc->rx_chan_mask);
- }
- }
- read_unlock(&mal_list_lock);
- return IRQ_HANDLED;
- }
- static irqreturn_t mal_txde(int irq, void *dev_instance, struct pt_regs *regs)
- {
- struct ibm_ocp_mal *mal = dev_instance;
- struct list_head *l;
- unsigned long deir;
- deir = get_mal_dcrn(mal, DCRN_MALTXDEIR);
- /* FIXME: print which MAL correctly */
- printk(KERN_WARNING "%s: Tx descriptor error (MALTXDEIR=%lx)\n",
- "MAL", deir);
- read_lock(&mal_list_lock);
- list_for_each(l, &mal->commac) {
- struct mal_commac *mc = list_entry(l, struct mal_commac, list);
- if (deir & mc->tx_chan_mask) {
- mc->ops->txde(mc->dev, deir & mc->tx_chan_mask);
- }
- }
- read_unlock(&mal_list_lock);
- return IRQ_HANDLED;
- }
- /*
- * This interrupt should be very rare at best. This occurs when
- * the hardware has a problem with the receive descriptors. The manual
- * states that it occurs when the hardware cannot the receive descriptor
- * empty bit is not set. The recovery mechanism will be to
- * traverse through the descriptors, handle any that are marked to be
- * handled and reinitialize each along the way. At that point the driver
- * will be restarted.
- */
- static irqreturn_t mal_rxde(int irq, void *dev_instance, struct pt_regs *regs)
- {
- struct ibm_ocp_mal *mal = dev_instance;
- struct list_head *l;
- unsigned long deir;
- deir = get_mal_dcrn(mal, DCRN_MALRXDEIR);
- /*
- * This really is needed. This case encountered in stress testing.
- */
- if (deir == 0)
- return IRQ_HANDLED;
- /* FIXME: print which MAL correctly */
- printk(KERN_WARNING "%s: Rx descriptor error (MALRXDEIR=%lx)\n",
- "MAL", deir);
- read_lock(&mal_list_lock);
- list_for_each(l, &mal->commac) {
- struct mal_commac *mc = list_entry(l, struct mal_commac, list);
- if (deir & mc->rx_chan_mask) {
- mc->ops->rxde(mc->dev, deir & mc->rx_chan_mask);
- }
- }
- read_unlock(&mal_list_lock);
- return IRQ_HANDLED;
- }
- static int __init mal_probe(struct ocp_device *ocpdev)
- {
- struct ibm_ocp_mal *mal = NULL;
- struct ocp_func_mal_data *maldata;
- int err = 0;
- maldata = (struct ocp_func_mal_data *)ocpdev->def->additions;
- if (maldata == NULL) {
- printk(KERN_ERR "mal%d: Missing additional datas !\n",
- ocpdev->def->index);
- return -ENODEV;
- }
- mal = kmalloc(sizeof(struct ibm_ocp_mal), GFP_KERNEL);
- if (mal == NULL) {
- printk(KERN_ERR
- "mal%d: Out of memory allocating MAL structure !\n",
- ocpdev->def->index);
- return -ENOMEM;
- }
- memset(mal, 0, sizeof(*mal));
- switch (ocpdev->def->index) {
- case 0:
- mal->dcrbase = DCRN_MAL_BASE;
- break;
- #ifdef DCRN_MAL1_BASE
- case 1:
- mal->dcrbase = DCRN_MAL1_BASE;
- break;
- #endif
- default:
- BUG();
- }
- /**************************/
- INIT_LIST_HEAD(&mal->commac);
- set_mal_dcrn(mal, DCRN_MALRXCARR, 0xFFFFFFFF);
- set_mal_dcrn(mal, DCRN_MALTXCARR, 0xFFFFFFFF);
- set_mal_dcrn(mal, DCRN_MALCR, MALCR_MMSR); /* 384 */
- /* FIXME: Add delay */
- /* Set the MAL configuration register */
- set_mal_dcrn(mal, DCRN_MALCR,
- MALCR_PLBB | MALCR_OPBBL | MALCR_LEA |
- MALCR_PLBLT_DEFAULT);
- /* It would be nice to allocate buffers separately for each
- * channel, but we can't because the channels share the upper
- * 13 bits of address lines. Each channels buffer must also
- * be 4k aligned, so we allocate 4k for each channel. This is
- * inefficient FIXME: do better, if possible */
- mal->tx_virt_addr = dma_alloc_coherent(&ocpdev->dev,
- MAL_DT_ALIGN *
- maldata->num_tx_chans,
- &mal->tx_phys_addr, GFP_KERNEL);
- if (mal->tx_virt_addr == NULL) {
- printk(KERN_ERR
- "mal%d: Out of memory allocating MAL descriptors !\n",
- ocpdev->def->index);
- err = -ENOMEM;
- goto fail;
- }
- /* God, oh, god, I hate DCRs */
- set_mal_dcrn(mal, DCRN_MALTXCTP0R, mal->tx_phys_addr);
- #ifdef DCRN_MALTXCTP1R
- if (maldata->num_tx_chans > 1)
- set_mal_dcrn(mal, DCRN_MALTXCTP1R,
- mal->tx_phys_addr + MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP1R */
- #ifdef DCRN_MALTXCTP2R
- if (maldata->num_tx_chans > 2)
- set_mal_dcrn(mal, DCRN_MALTXCTP2R,
- mal->tx_phys_addr + 2 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP2R */
- #ifdef DCRN_MALTXCTP3R
- if (maldata->num_tx_chans > 3)
- set_mal_dcrn(mal, DCRN_MALTXCTP3R,
- mal->tx_phys_addr + 3 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP3R */
- #ifdef DCRN_MALTXCTP4R
- if (maldata->num_tx_chans > 4)
- set_mal_dcrn(mal, DCRN_MALTXCTP4R,
- mal->tx_phys_addr + 4 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP4R */
- #ifdef DCRN_MALTXCTP5R
- if (maldata->num_tx_chans > 5)
- set_mal_dcrn(mal, DCRN_MALTXCTP5R,
- mal->tx_phys_addr + 5 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP5R */
- #ifdef DCRN_MALTXCTP6R
- if (maldata->num_tx_chans > 6)
- set_mal_dcrn(mal, DCRN_MALTXCTP6R,
- mal->tx_phys_addr + 6 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP6R */
- #ifdef DCRN_MALTXCTP7R
- if (maldata->num_tx_chans > 7)
- set_mal_dcrn(mal, DCRN_MALTXCTP7R,
- mal->tx_phys_addr + 7 * MAL_DT_ALIGN);
- #endif /* DCRN_MALTXCTP7R */
- mal->rx_virt_addr = dma_alloc_coherent(&ocpdev->dev,
- MAL_DT_ALIGN *
- maldata->num_rx_chans,
- &mal->rx_phys_addr, GFP_KERNEL);
- set_mal_dcrn(mal, DCRN_MALRXCTP0R, mal->rx_phys_addr);
- #ifdef DCRN_MALRXCTP1R
- if (maldata->num_rx_chans > 1)
- set_mal_dcrn(mal, DCRN_MALRXCTP1R,
- mal->rx_phys_addr + MAL_DT_ALIGN);
- #endif /* DCRN_MALRXCTP1R */
- #ifdef DCRN_MALRXCTP2R
- if (maldata->num_rx_chans > 2)
- set_mal_dcrn(mal, DCRN_MALRXCTP2R,
- mal->rx_phys_addr + 2 * MAL_DT_ALIGN);
- #endif /* DCRN_MALRXCTP2R */
- #ifdef DCRN_MALRXCTP3R
- if (maldata->num_rx_chans > 3)
- set_mal_dcrn(mal, DCRN_MALRXCTP3R,
- mal->rx_phys_addr + 3 * MAL_DT_ALIGN);
- #endif /* DCRN_MALRXCTP3R */
- err = request_irq(maldata->serr_irq, mal_serr, 0, "MAL SERR", mal);
- if (err)
- goto fail;
- err = request_irq(maldata->txde_irq, mal_txde, 0, "MAL TX DE ", mal);
- if (err)
- goto fail;
- err = request_irq(maldata->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal);
- if (err)
- goto fail;
- err = request_irq(maldata->rxde_irq, mal_rxde, 0, "MAL RX DE", mal);
- if (err)
- goto fail;
- err = request_irq(maldata->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal);
- if (err)
- goto fail;
- set_mal_dcrn(mal, DCRN_MALIER,
- MALIER_DE | MALIER_NE | MALIER_TE |
- MALIER_OPBE | MALIER_PLBE);
- /* Advertise me to the rest of the world */
- ocp_set_drvdata(ocpdev, mal);
- printk(KERN_INFO "mal%d: Initialized, %d tx channels, %d rx channels\n",
- ocpdev->def->index, maldata->num_tx_chans,
- maldata->num_rx_chans);
- return 0;
- fail:
- /* FIXME: dispose requested IRQs ! */
- if (err && mal)
- kfree(mal);
- return err;
- }
- static void __exit mal_remove(struct ocp_device *ocpdev)
- {
- struct ibm_ocp_mal *mal = ocp_get_drvdata(ocpdev);
- struct ocp_func_mal_data *maldata = ocpdev->def->additions;
- BUG_ON(!maldata);
- ocp_set_drvdata(ocpdev, NULL);
- /* FIXME: shut down the MAL, deal with dependency with emac */
- free_irq(maldata->serr_irq, mal);
- free_irq(maldata->txde_irq, mal);
- free_irq(maldata->txeob_irq, mal);
- free_irq(maldata->rxde_irq, mal);
- free_irq(maldata->rxeob_irq, mal);
- if (mal->tx_virt_addr)
- dma_free_coherent(&ocpdev->dev,
- MAL_DT_ALIGN * maldata->num_tx_chans,
- mal->tx_virt_addr, mal->tx_phys_addr);
- if (mal->rx_virt_addr)
- dma_free_coherent(&ocpdev->dev,
- MAL_DT_ALIGN * maldata->num_rx_chans,
- mal->rx_virt_addr, mal->rx_phys_addr);
- kfree(mal);
- }
- /* Structure for a device driver */
- static struct ocp_device_id mal_ids[] = {
- {.vendor = OCP_ANY_ID,.function = OCP_FUNC_MAL},
- {.vendor = OCP_VENDOR_INVALID}
- };
- static struct ocp_driver mal_driver = {
- .name = "mal",
- .id_table = mal_ids,
- .probe = mal_probe,
- .remove = mal_remove,
- };
- static int __init init_mals(void)
- {
- int rc;
- rc = ocp_register_driver(&mal_driver);
- if (rc < 0) {
- ocp_unregister_driver(&mal_driver);
- return -ENODEV;
- }
- return 0;
- }
- static void __exit exit_mals(void)
- {
- ocp_unregister_driver(&mal_driver);
- }
- module_init(init_mals);
- module_exit(exit_mals);
|