162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/ethernet/ibm/emac/mal.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Memory Access Layer (MAL) support
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
862306a36Sopenharmony_ci *                <benh@kernel.crashing.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Based on the arch/ppc version of the driver:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Zultys Technologies.
1362306a36Sopenharmony_ci * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Based on original work by
1662306a36Sopenharmony_ci *      Benjamin Herrenschmidt <benh@kernel.crashing.org>,
1762306a36Sopenharmony_ci *      David Gibson <hermes@gibson.dropbear.id.au>,
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      Armin Kuster <akuster@mvista.com>
2062306a36Sopenharmony_ci *      Copyright 2002 MontaVista Softare Inc.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/of.h>
2662306a36Sopenharmony_ci#include <linux/of_irq.h>
2762306a36Sopenharmony_ci#include <linux/platform_device.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "core.h"
3062306a36Sopenharmony_ci#include <asm/dcr-regs.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int mal_count;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciint mal_register_commac(struct mal_instance *mal, struct mal_commac *commac)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	unsigned long flags;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	MAL_DBG(mal, "reg(%08x, %08x)" NL,
4162306a36Sopenharmony_ci		commac->tx_chan_mask, commac->rx_chan_mask);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Don't let multiple commacs claim the same channel(s) */
4462306a36Sopenharmony_ci	if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
4562306a36Sopenharmony_ci	    (mal->rx_chan_mask & commac->rx_chan_mask)) {
4662306a36Sopenharmony_ci		spin_unlock_irqrestore(&mal->lock, flags);
4762306a36Sopenharmony_ci		printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
4862306a36Sopenharmony_ci		       mal->index);
4962306a36Sopenharmony_ci		return -EBUSY;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (list_empty(&mal->list))
5362306a36Sopenharmony_ci		napi_enable(&mal->napi);
5462306a36Sopenharmony_ci	mal->tx_chan_mask |= commac->tx_chan_mask;
5562306a36Sopenharmony_ci	mal->rx_chan_mask |= commac->rx_chan_mask;
5662306a36Sopenharmony_ci	list_add(&commac->list, &mal->list);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_civoid mal_unregister_commac(struct mal_instance	*mal,
6462306a36Sopenharmony_ci		struct mal_commac *commac)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	unsigned long flags;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	MAL_DBG(mal, "unreg(%08x, %08x)" NL,
7162306a36Sopenharmony_ci		commac->tx_chan_mask, commac->rx_chan_mask);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mal->tx_chan_mask &= ~commac->tx_chan_mask;
7462306a36Sopenharmony_ci	mal->rx_chan_mask &= ~commac->rx_chan_mask;
7562306a36Sopenharmony_ci	list_del_init(&commac->list);
7662306a36Sopenharmony_ci	if (list_empty(&mal->list))
7762306a36Sopenharmony_ci		napi_disable(&mal->napi);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_rx_chans ||
8562306a36Sopenharmony_ci	       size > MAL_MAX_RX_SIZE);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	MAL_DBG(mal, "set_rbcs(%d, %lu)" NL, channel, size);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (size & 0xf) {
9062306a36Sopenharmony_ci		printk(KERN_WARNING
9162306a36Sopenharmony_ci		       "mal%d: incorrect RX size %lu for the channel %d\n",
9262306a36Sopenharmony_ci		       mal->index, size, channel);
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_RCBS(channel), size >> 4);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciint mal_tx_bd_offset(struct mal_instance *mal, int channel)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_tx_chans);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return channel * NUM_TX_BUFF;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciint mal_rx_bd_offset(struct mal_instance *mal, int channel)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_rx_chans);
11062306a36Sopenharmony_ci	return mal->num_tx_chans * NUM_TX_BUFF + channel * NUM_RX_BUFF;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_civoid mal_enable_tx_channel(struct mal_instance *mal, int channel)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned long flags;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	MAL_DBG(mal, "enable_tx(%d)" NL, channel);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXCASR,
12262306a36Sopenharmony_ci		     get_mal_dcrn(mal, MAL_TXCASR) | MAL_CHAN_MASK(channel));
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid mal_disable_tx_channel(struct mal_instance *mal, int channel)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXCARR, MAL_CHAN_MASK(channel));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	MAL_DBG(mal, "disable_tx(%d)" NL, channel);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid mal_enable_rx_channel(struct mal_instance *mal, int channel)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	unsigned long flags;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple
14062306a36Sopenharmony_ci	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value
14162306a36Sopenharmony_ci	 * for the bitmask
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	if (!(channel % 8))
14462306a36Sopenharmony_ci		channel >>= 3;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	MAL_DBG(mal, "enable_rx(%d)" NL, channel);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXCASR,
15162306a36Sopenharmony_ci		     get_mal_dcrn(mal, MAL_RXCASR) | MAL_CHAN_MASK(channel));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid mal_disable_rx_channel(struct mal_instance *mal, int channel)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	/*
15962306a36Sopenharmony_ci	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple
16062306a36Sopenharmony_ci	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value
16162306a36Sopenharmony_ci	 * for the bitmask
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	if (!(channel % 8))
16462306a36Sopenharmony_ci		channel >>= 3;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	MAL_DBG(mal, "disable_rx(%d)" NL, channel);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid mal_poll_add(struct mal_instance *mal, struct mal_commac *commac)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	unsigned long flags;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	MAL_DBG(mal, "poll_add(%p)" NL, commac);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* starts disabled */
18062306a36Sopenharmony_ci	set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	list_add_tail(&commac->poll_list, &mal->poll_list);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid mal_poll_del(struct mal_instance *mal, struct mal_commac *commac)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	unsigned long flags;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	MAL_DBG(mal, "poll_del(%p)" NL, commac);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	list_del(&commac->poll_list);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/* synchronized by mal_poll() */
20162306a36Sopenharmony_cistatic inline void mal_enable_eob_irq(struct mal_instance *mal)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	MAL_DBG2(mal, "enable_irq" NL);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
20662306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) | MAL_CFG_EOPIE);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* synchronized by NAPI state */
21062306a36Sopenharmony_cistatic inline void mal_disable_eob_irq(struct mal_instance *mal)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
21362306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) & ~MAL_CFG_EOPIE);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	MAL_DBG2(mal, "disable_irq" NL);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic irqreturn_t mal_serr(int irq, void *dev_instance)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	u32 esr = get_mal_dcrn(mal, MAL_ESR);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Clear the error status register */
22562306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_ESR, esr);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	MAL_DBG(mal, "SERR %08x" NL, esr);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (esr & MAL_ESR_EVB) {
23062306a36Sopenharmony_ci		if (esr & MAL_ESR_DE) {
23162306a36Sopenharmony_ci			/* We ignore Descriptor error,
23262306a36Sopenharmony_ci			 * TXDE or RXDE interrupt will be generated anyway.
23362306a36Sopenharmony_ci			 */
23462306a36Sopenharmony_ci			return IRQ_HANDLED;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		if (esr & MAL_ESR_PEIN) {
23862306a36Sopenharmony_ci			/* PLB error, it's probably buggy hardware or
23962306a36Sopenharmony_ci			 * incorrect physical address in BD (i.e. bug)
24062306a36Sopenharmony_ci			 */
24162306a36Sopenharmony_ci			if (net_ratelimit())
24262306a36Sopenharmony_ci				printk(KERN_ERR
24362306a36Sopenharmony_ci				       "mal%d: system error, "
24462306a36Sopenharmony_ci				       "PLB (ESR = 0x%08x)\n",
24562306a36Sopenharmony_ci				       mal->index, esr);
24662306a36Sopenharmony_ci			return IRQ_HANDLED;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		/* OPB error, it's probably buggy hardware or incorrect
25062306a36Sopenharmony_ci		 * EBC setup
25162306a36Sopenharmony_ci		 */
25262306a36Sopenharmony_ci		if (net_ratelimit())
25362306a36Sopenharmony_ci			printk(KERN_ERR
25462306a36Sopenharmony_ci			       "mal%d: system error, OPB (ESR = 0x%08x)\n",
25562306a36Sopenharmony_ci			       mal->index, esr);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	return IRQ_HANDLED;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic inline void mal_schedule_poll(struct mal_instance *mal)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	if (likely(napi_schedule_prep(&mal->napi))) {
26362306a36Sopenharmony_ci		MAL_DBG2(mal, "schedule_poll" NL);
26462306a36Sopenharmony_ci		spin_lock(&mal->lock);
26562306a36Sopenharmony_ci		mal_disable_eob_irq(mal);
26662306a36Sopenharmony_ci		spin_unlock(&mal->lock);
26762306a36Sopenharmony_ci		__napi_schedule(&mal->napi);
26862306a36Sopenharmony_ci	} else
26962306a36Sopenharmony_ci		MAL_DBG2(mal, "already in poll" NL);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic irqreturn_t mal_txeob(int irq, void *dev_instance)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	u32 r = get_mal_dcrn(mal, MAL_TXEOBISR);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	MAL_DBG2(mal, "txeob %08x" NL, r);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mal_schedule_poll(mal);
28162306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXEOBISR, r);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR_NATIVE
28462306a36Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
28562306a36Sopenharmony_ci		mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
28662306a36Sopenharmony_ci				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
28762306a36Sopenharmony_ci#endif
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return IRQ_HANDLED;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic irqreturn_t mal_rxeob(int irq, void *dev_instance)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	u32 r = get_mal_dcrn(mal, MAL_RXEOBISR);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	MAL_DBG2(mal, "rxeob %08x" NL, r);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	mal_schedule_poll(mal);
30162306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXEOBISR, r);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR_NATIVE
30462306a36Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
30562306a36Sopenharmony_ci		mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
30662306a36Sopenharmony_ci				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
30762306a36Sopenharmony_ci#endif
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return IRQ_HANDLED;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic irqreturn_t mal_txde(int irq, void *dev_instance)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	u32 deir = get_mal_dcrn(mal, MAL_TXDEIR);
31762306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXDEIR, deir);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	MAL_DBG(mal, "txde %08x" NL, deir);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (net_ratelimit())
32262306a36Sopenharmony_ci		printk(KERN_ERR
32362306a36Sopenharmony_ci		       "mal%d: TX descriptor error (TXDEIR = 0x%08x)\n",
32462306a36Sopenharmony_ci		       mal->index, deir);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return IRQ_HANDLED;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic irqreturn_t mal_rxde(int irq, void *dev_instance)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
33262306a36Sopenharmony_ci	struct list_head *l;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	u32 deir = get_mal_dcrn(mal, MAL_RXDEIR);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	MAL_DBG(mal, "rxde %08x" NL, deir);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	list_for_each(l, &mal->list) {
33962306a36Sopenharmony_ci		struct mal_commac *mc = list_entry(l, struct mal_commac, list);
34062306a36Sopenharmony_ci		if (deir & mc->rx_chan_mask) {
34162306a36Sopenharmony_ci			set_bit(MAL_COMMAC_RX_STOPPED, &mc->flags);
34262306a36Sopenharmony_ci			mc->ops->rxde(mc->dev);
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	mal_schedule_poll(mal);
34762306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXDEIR, deir);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return IRQ_HANDLED;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic irqreturn_t mal_int(int irq, void *dev_instance)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct mal_instance *mal = dev_instance;
35562306a36Sopenharmony_ci	u32 esr = get_mal_dcrn(mal, MAL_ESR);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (esr & MAL_ESR_EVB) {
35862306a36Sopenharmony_ci		/* descriptor error */
35962306a36Sopenharmony_ci		if (esr & MAL_ESR_DE) {
36062306a36Sopenharmony_ci			if (esr & MAL_ESR_CIDT)
36162306a36Sopenharmony_ci				return mal_rxde(irq, dev_instance);
36262306a36Sopenharmony_ci			else
36362306a36Sopenharmony_ci				return mal_txde(irq, dev_instance);
36462306a36Sopenharmony_ci		} else { /* SERR */
36562306a36Sopenharmony_ci			return mal_serr(irq, dev_instance);
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	return IRQ_HANDLED;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_civoid mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	/* Spinlock-type semantics: only one caller disable poll at a time */
37462306a36Sopenharmony_ci	while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
37562306a36Sopenharmony_ci		msleep(1);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Synchronize with the MAL NAPI poller */
37862306a36Sopenharmony_ci	napi_synchronize(&mal->napi);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_civoid mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	smp_wmb();
38462306a36Sopenharmony_ci	clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Feels better to trigger a poll here to catch up with events that
38762306a36Sopenharmony_ci	 * may have happened on this channel while disabled. It will most
38862306a36Sopenharmony_ci	 * probably be delayed until the next interrupt but that's mostly a
38962306a36Sopenharmony_ci	 * non-issue in the context where this is called.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	napi_schedule(&mal->napi);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int mal_poll(struct napi_struct *napi, int budget)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct mal_instance *mal = container_of(napi, struct mal_instance, napi);
39762306a36Sopenharmony_ci	struct list_head *l;
39862306a36Sopenharmony_ci	int received = 0;
39962306a36Sopenharmony_ci	unsigned long flags;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	MAL_DBG2(mal, "poll(%d)" NL, budget);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Process TX skbs */
40462306a36Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
40562306a36Sopenharmony_ci		struct mal_commac *mc =
40662306a36Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
40762306a36Sopenharmony_ci		mc->ops->poll_tx(mc->dev);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Process RX skbs.
41162306a36Sopenharmony_ci	 *
41262306a36Sopenharmony_ci	 * We _might_ need something more smart here to enforce polling
41362306a36Sopenharmony_ci	 * fairness.
41462306a36Sopenharmony_ci	 */
41562306a36Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
41662306a36Sopenharmony_ci		struct mal_commac *mc =
41762306a36Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
41862306a36Sopenharmony_ci		int n;
41962306a36Sopenharmony_ci		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
42062306a36Sopenharmony_ci			continue;
42162306a36Sopenharmony_ci		n = mc->ops->poll_rx(mc->dev, budget - received);
42262306a36Sopenharmony_ci		if (n) {
42362306a36Sopenharmony_ci			received += n;
42462306a36Sopenharmony_ci			if (received >= budget)
42562306a36Sopenharmony_ci				return budget;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (napi_complete_done(napi, received)) {
43062306a36Sopenharmony_ci		/* We need to disable IRQs to protect from RXDE IRQ here */
43162306a36Sopenharmony_ci		spin_lock_irqsave(&mal->lock, flags);
43262306a36Sopenharmony_ci		mal_enable_eob_irq(mal);
43362306a36Sopenharmony_ci		spin_unlock_irqrestore(&mal->lock, flags);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* Check for "rotting" packet(s) */
43762306a36Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
43862306a36Sopenharmony_ci		struct mal_commac *mc =
43962306a36Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
44062306a36Sopenharmony_ci		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
44162306a36Sopenharmony_ci			continue;
44262306a36Sopenharmony_ci		if (unlikely(mc->ops->peek_rx(mc->dev) ||
44362306a36Sopenharmony_ci			     test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) {
44462306a36Sopenharmony_ci			MAL_DBG2(mal, "rotting packet" NL);
44562306a36Sopenharmony_ci			if (!napi_reschedule(napi))
44662306a36Sopenharmony_ci				goto more_work;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci			spin_lock_irqsave(&mal->lock, flags);
44962306a36Sopenharmony_ci			mal_disable_eob_irq(mal);
45062306a36Sopenharmony_ci			spin_unlock_irqrestore(&mal->lock, flags);
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci		mc->ops->poll_tx(mc->dev);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci more_work:
45662306a36Sopenharmony_ci	MAL_DBG2(mal, "poll() %d <- %d" NL, budget, received);
45762306a36Sopenharmony_ci	return received;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void mal_reset(struct mal_instance *mal)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	int n = 10;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	MAL_DBG(mal, "reset" NL);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, MAL_CFG_SR);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* Wait for reset to complete (1 system clock) */
46962306a36Sopenharmony_ci	while ((get_mal_dcrn(mal, MAL_CFG) & MAL_CFG_SR) && n)
47062306a36Sopenharmony_ci		--n;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (unlikely(!n))
47362306a36Sopenharmony_ci		printk(KERN_ERR "mal%d: reset timeout\n", mal->index);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ciint mal_get_regs_len(struct mal_instance *mal)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	return sizeof(struct emac_ethtool_regs_subhdr) +
47962306a36Sopenharmony_ci	    sizeof(struct mal_regs);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_civoid *mal_dump_regs(struct mal_instance *mal, void *buf)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct emac_ethtool_regs_subhdr *hdr = buf;
48562306a36Sopenharmony_ci	struct mal_regs *regs = (struct mal_regs *)(hdr + 1);
48662306a36Sopenharmony_ci	int i;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	hdr->version = mal->version;
48962306a36Sopenharmony_ci	hdr->index = mal->index;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	regs->tx_count = mal->num_tx_chans;
49262306a36Sopenharmony_ci	regs->rx_count = mal->num_rx_chans;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	regs->cfg = get_mal_dcrn(mal, MAL_CFG);
49562306a36Sopenharmony_ci	regs->esr = get_mal_dcrn(mal, MAL_ESR);
49662306a36Sopenharmony_ci	regs->ier = get_mal_dcrn(mal, MAL_IER);
49762306a36Sopenharmony_ci	regs->tx_casr = get_mal_dcrn(mal, MAL_TXCASR);
49862306a36Sopenharmony_ci	regs->tx_carr = get_mal_dcrn(mal, MAL_TXCARR);
49962306a36Sopenharmony_ci	regs->tx_eobisr = get_mal_dcrn(mal, MAL_TXEOBISR);
50062306a36Sopenharmony_ci	regs->tx_deir = get_mal_dcrn(mal, MAL_TXDEIR);
50162306a36Sopenharmony_ci	regs->rx_casr = get_mal_dcrn(mal, MAL_RXCASR);
50262306a36Sopenharmony_ci	regs->rx_carr = get_mal_dcrn(mal, MAL_RXCARR);
50362306a36Sopenharmony_ci	regs->rx_eobisr = get_mal_dcrn(mal, MAL_RXEOBISR);
50462306a36Sopenharmony_ci	regs->rx_deir = get_mal_dcrn(mal, MAL_RXDEIR);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	for (i = 0; i < regs->tx_count; ++i)
50762306a36Sopenharmony_ci		regs->tx_ctpr[i] = get_mal_dcrn(mal, MAL_TXCTPR(i));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for (i = 0; i < regs->rx_count; ++i) {
51062306a36Sopenharmony_ci		regs->rx_ctpr[i] = get_mal_dcrn(mal, MAL_RXCTPR(i));
51162306a36Sopenharmony_ci		regs->rcbs[i] = get_mal_dcrn(mal, MAL_RCBS(i));
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci	return regs + 1;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int mal_probe(struct platform_device *ofdev)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct mal_instance *mal;
51962306a36Sopenharmony_ci	int err = 0, i, bd_size;
52062306a36Sopenharmony_ci	int index = mal_count++;
52162306a36Sopenharmony_ci	unsigned int dcr_base;
52262306a36Sopenharmony_ci	const u32 *prop;
52362306a36Sopenharmony_ci	u32 cfg;
52462306a36Sopenharmony_ci	unsigned long irqflags;
52562306a36Sopenharmony_ci	irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	mal = kzalloc(sizeof(struct mal_instance), GFP_KERNEL);
52862306a36Sopenharmony_ci	if (!mal)
52962306a36Sopenharmony_ci		return -ENOMEM;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mal->index = index;
53262306a36Sopenharmony_ci	mal->ofdev = ofdev;
53362306a36Sopenharmony_ci	mal->version = of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal2") ? 2 : 1;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	MAL_DBG(mal, "probe" NL);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	prop = of_get_property(ofdev->dev.of_node, "num-tx-chans", NULL);
53862306a36Sopenharmony_ci	if (prop == NULL) {
53962306a36Sopenharmony_ci		printk(KERN_ERR
54062306a36Sopenharmony_ci		       "mal%d: can't find MAL num-tx-chans property!\n",
54162306a36Sopenharmony_ci		       index);
54262306a36Sopenharmony_ci		err = -ENODEV;
54362306a36Sopenharmony_ci		goto fail;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	mal->num_tx_chans = prop[0];
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL);
54862306a36Sopenharmony_ci	if (prop == NULL) {
54962306a36Sopenharmony_ci		printk(KERN_ERR
55062306a36Sopenharmony_ci		       "mal%d: can't find MAL num-rx-chans property!\n",
55162306a36Sopenharmony_ci		       index);
55262306a36Sopenharmony_ci		err = -ENODEV;
55362306a36Sopenharmony_ci		goto fail;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	mal->num_rx_chans = prop[0];
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	dcr_base = dcr_resource_start(ofdev->dev.of_node, 0);
55862306a36Sopenharmony_ci	if (dcr_base == 0) {
55962306a36Sopenharmony_ci		printk(KERN_ERR
56062306a36Sopenharmony_ci		       "mal%d: can't find DCR resource!\n", index);
56162306a36Sopenharmony_ci		err = -ENODEV;
56262306a36Sopenharmony_ci		goto fail;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	mal->dcr_host = dcr_map(ofdev->dev.of_node, dcr_base, 0x100);
56562306a36Sopenharmony_ci	if (!DCR_MAP_OK(mal->dcr_host)) {
56662306a36Sopenharmony_ci		printk(KERN_ERR
56762306a36Sopenharmony_ci		       "mal%d: failed to map DCRs !\n", index);
56862306a36Sopenharmony_ci		err = -ENODEV;
56962306a36Sopenharmony_ci		goto fail;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-405ez")) {
57362306a36Sopenharmony_ci#if defined(CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT) && \
57462306a36Sopenharmony_ci		defined(CONFIG_IBM_EMAC_MAL_COMMON_ERR)
57562306a36Sopenharmony_ci		mal->features |= (MAL_FTR_CLEAR_ICINTSTAT |
57662306a36Sopenharmony_ci				MAL_FTR_COMMON_ERR_INT);
57762306a36Sopenharmony_ci#else
57862306a36Sopenharmony_ci		printk(KERN_ERR "%pOF: Support for 405EZ not enabled!\n",
57962306a36Sopenharmony_ci				ofdev->dev.of_node);
58062306a36Sopenharmony_ci		err = -ENODEV;
58162306a36Sopenharmony_ci		goto fail;
58262306a36Sopenharmony_ci#endif
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	mal->txeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
58662306a36Sopenharmony_ci	mal->rxeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 1);
58762306a36Sopenharmony_ci	mal->serr_irq = irq_of_parse_and_map(ofdev->dev.of_node, 2);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) {
59062306a36Sopenharmony_ci		mal->txde_irq = mal->rxde_irq = mal->serr_irq;
59162306a36Sopenharmony_ci	} else {
59262306a36Sopenharmony_ci		mal->txde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 3);
59362306a36Sopenharmony_ci		mal->rxde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 4);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (!mal->txeob_irq || !mal->rxeob_irq || !mal->serr_irq ||
59762306a36Sopenharmony_ci	    !mal->txde_irq  || !mal->rxde_irq) {
59862306a36Sopenharmony_ci		printk(KERN_ERR
59962306a36Sopenharmony_ci		       "mal%d: failed to map interrupts !\n", index);
60062306a36Sopenharmony_ci		err = -ENODEV;
60162306a36Sopenharmony_ci		goto fail_unmap;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	INIT_LIST_HEAD(&mal->poll_list);
60562306a36Sopenharmony_ci	INIT_LIST_HEAD(&mal->list);
60662306a36Sopenharmony_ci	spin_lock_init(&mal->lock);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	init_dummy_netdev(&mal->dummy_dev);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	netif_napi_add_weight(&mal->dummy_dev, &mal->napi, mal_poll,
61162306a36Sopenharmony_ci			      CONFIG_IBM_EMAC_POLL_WEIGHT);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* Load power-on reset defaults */
61462306a36Sopenharmony_ci	mal_reset(mal);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* Set the MAL configuration register */
61762306a36Sopenharmony_ci	cfg = (mal->version == 2) ? MAL2_CFG_DEFAULT : MAL1_CFG_DEFAULT;
61862306a36Sopenharmony_ci	cfg |= MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Current Axon is not happy with priority being non-0, it can
62162306a36Sopenharmony_ci	 * deadlock, fix it up here
62262306a36Sopenharmony_ci	 */
62362306a36Sopenharmony_ci	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-axon"))
62462306a36Sopenharmony_ci		cfg &= ~(MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Apply configuration */
62762306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, cfg);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Allocate space for BD rings */
63062306a36Sopenharmony_ci	BUG_ON(mal->num_tx_chans <= 0 || mal->num_tx_chans > 32);
63162306a36Sopenharmony_ci	BUG_ON(mal->num_rx_chans <= 0 || mal->num_rx_chans > 32);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	bd_size = sizeof(struct mal_descriptor) *
63462306a36Sopenharmony_ci		(NUM_TX_BUFF * mal->num_tx_chans +
63562306a36Sopenharmony_ci		 NUM_RX_BUFF * mal->num_rx_chans);
63662306a36Sopenharmony_ci	mal->bd_virt = dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma,
63762306a36Sopenharmony_ci					  GFP_KERNEL);
63862306a36Sopenharmony_ci	if (mal->bd_virt == NULL) {
63962306a36Sopenharmony_ci		err = -ENOMEM;
64062306a36Sopenharmony_ci		goto fail_unmap;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	for (i = 0; i < mal->num_tx_chans; ++i)
64462306a36Sopenharmony_ci		set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma +
64562306a36Sopenharmony_ci			     sizeof(struct mal_descriptor) *
64662306a36Sopenharmony_ci			     mal_tx_bd_offset(mal, i));
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	for (i = 0; i < mal->num_rx_chans; ++i)
64962306a36Sopenharmony_ci		set_mal_dcrn(mal, MAL_RXCTPR(i), mal->bd_dma +
65062306a36Sopenharmony_ci			     sizeof(struct mal_descriptor) *
65162306a36Sopenharmony_ci			     mal_rx_bd_offset(mal, i));
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) {
65462306a36Sopenharmony_ci		irqflags = IRQF_SHARED;
65562306a36Sopenharmony_ci		hdlr_serr = hdlr_txde = hdlr_rxde = mal_int;
65662306a36Sopenharmony_ci	} else {
65762306a36Sopenharmony_ci		irqflags = 0;
65862306a36Sopenharmony_ci		hdlr_serr = mal_serr;
65962306a36Sopenharmony_ci		hdlr_txde = mal_txde;
66062306a36Sopenharmony_ci		hdlr_rxde = mal_rxde;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	err = request_irq(mal->serr_irq, hdlr_serr, irqflags, "MAL SERR", mal);
66462306a36Sopenharmony_ci	if (err)
66562306a36Sopenharmony_ci		goto fail2;
66662306a36Sopenharmony_ci	err = request_irq(mal->txde_irq, hdlr_txde, irqflags, "MAL TX DE", mal);
66762306a36Sopenharmony_ci	if (err)
66862306a36Sopenharmony_ci		goto fail3;
66962306a36Sopenharmony_ci	err = request_irq(mal->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal);
67062306a36Sopenharmony_ci	if (err)
67162306a36Sopenharmony_ci		goto fail4;
67262306a36Sopenharmony_ci	err = request_irq(mal->rxde_irq, hdlr_rxde, irqflags, "MAL RX DE", mal);
67362306a36Sopenharmony_ci	if (err)
67462306a36Sopenharmony_ci		goto fail5;
67562306a36Sopenharmony_ci	err = request_irq(mal->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal);
67662306a36Sopenharmony_ci	if (err)
67762306a36Sopenharmony_ci		goto fail6;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* Enable all MAL SERR interrupt sources */
68062306a36Sopenharmony_ci	set_mal_dcrn(mal, MAL_IER, MAL_IER_EVENTS);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* Enable EOB interrupt */
68362306a36Sopenharmony_ci	mal_enable_eob_irq(mal);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	printk(KERN_INFO
68662306a36Sopenharmony_ci	       "MAL v%d %pOF, %d TX channels, %d RX channels\n",
68762306a36Sopenharmony_ci	       mal->version, ofdev->dev.of_node,
68862306a36Sopenharmony_ci	       mal->num_tx_chans, mal->num_rx_chans);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* Advertise this instance to the rest of the world */
69162306a36Sopenharmony_ci	wmb();
69262306a36Sopenharmony_ci	platform_set_drvdata(ofdev, mal);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return 0;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci fail6:
69762306a36Sopenharmony_ci	free_irq(mal->rxde_irq, mal);
69862306a36Sopenharmony_ci fail5:
69962306a36Sopenharmony_ci	free_irq(mal->txeob_irq, mal);
70062306a36Sopenharmony_ci fail4:
70162306a36Sopenharmony_ci	free_irq(mal->txde_irq, mal);
70262306a36Sopenharmony_ci fail3:
70362306a36Sopenharmony_ci	free_irq(mal->serr_irq, mal);
70462306a36Sopenharmony_ci fail2:
70562306a36Sopenharmony_ci	dma_free_coherent(&ofdev->dev, bd_size, mal->bd_virt, mal->bd_dma);
70662306a36Sopenharmony_ci fail_unmap:
70762306a36Sopenharmony_ci	dcr_unmap(mal->dcr_host, 0x100);
70862306a36Sopenharmony_ci fail:
70962306a36Sopenharmony_ci	kfree(mal);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return err;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int mal_remove(struct platform_device *ofdev)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct mal_instance *mal = platform_get_drvdata(ofdev);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	MAL_DBG(mal, "remove" NL);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* Synchronize with scheduled polling */
72162306a36Sopenharmony_ci	napi_disable(&mal->napi);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (!list_empty(&mal->list))
72462306a36Sopenharmony_ci		/* This is *very* bad */
72562306a36Sopenharmony_ci		WARN(1, KERN_EMERG
72662306a36Sopenharmony_ci		       "mal%d: commac list is not empty on remove!\n",
72762306a36Sopenharmony_ci		       mal->index);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	free_irq(mal->serr_irq, mal);
73062306a36Sopenharmony_ci	free_irq(mal->txde_irq, mal);
73162306a36Sopenharmony_ci	free_irq(mal->txeob_irq, mal);
73262306a36Sopenharmony_ci	free_irq(mal->rxde_irq, mal);
73362306a36Sopenharmony_ci	free_irq(mal->rxeob_irq, mal);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	mal_reset(mal);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	dma_free_coherent(&ofdev->dev,
73862306a36Sopenharmony_ci			  sizeof(struct mal_descriptor) *
73962306a36Sopenharmony_ci			  (NUM_TX_BUFF * mal->num_tx_chans +
74062306a36Sopenharmony_ci			   NUM_RX_BUFF * mal->num_rx_chans), mal->bd_virt,
74162306a36Sopenharmony_ci			  mal->bd_dma);
74262306a36Sopenharmony_ci	kfree(mal);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	return 0;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic const struct of_device_id mal_platform_match[] =
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	{
75062306a36Sopenharmony_ci		.compatible	= "ibm,mcmal",
75162306a36Sopenharmony_ci	},
75262306a36Sopenharmony_ci	{
75362306a36Sopenharmony_ci		.compatible	= "ibm,mcmal2",
75462306a36Sopenharmony_ci	},
75562306a36Sopenharmony_ci	/* Backward compat */
75662306a36Sopenharmony_ci	{
75762306a36Sopenharmony_ci		.type		= "mcmal-dma",
75862306a36Sopenharmony_ci		.compatible	= "ibm,mcmal",
75962306a36Sopenharmony_ci	},
76062306a36Sopenharmony_ci	{
76162306a36Sopenharmony_ci		.type		= "mcmal-dma",
76262306a36Sopenharmony_ci		.compatible	= "ibm,mcmal2",
76362306a36Sopenharmony_ci	},
76462306a36Sopenharmony_ci	{},
76562306a36Sopenharmony_ci};
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic struct platform_driver mal_of_driver = {
76862306a36Sopenharmony_ci	.driver = {
76962306a36Sopenharmony_ci		.name = "mcmal",
77062306a36Sopenharmony_ci		.of_match_table = mal_platform_match,
77162306a36Sopenharmony_ci	},
77262306a36Sopenharmony_ci	.probe = mal_probe,
77362306a36Sopenharmony_ci	.remove = mal_remove,
77462306a36Sopenharmony_ci};
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ciint __init mal_init(void)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	return platform_driver_register(&mal_of_driver);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_civoid mal_exit(void)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	platform_driver_unregister(&mal_of_driver);
78462306a36Sopenharmony_ci}
785