18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/net/ethernet/ibm/emac/mal.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Memory Access Layer (MAL) support
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
88c2ecf20Sopenharmony_ci *                <benh@kernel.crashing.org>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Based on the arch/ppc version of the driver:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Copyright (c) 2004, 2005 Zultys Technologies.
138c2ecf20Sopenharmony_ci * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Based on original work by
168c2ecf20Sopenharmony_ci *      Benjamin Herrenschmidt <benh@kernel.crashing.org>,
178c2ecf20Sopenharmony_ci *      David Gibson <hermes@gibson.dropbear.id.au>,
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *      Armin Kuster <akuster@mvista.com>
208c2ecf20Sopenharmony_ci *      Copyright 2002 MontaVista Softare Inc.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "core.h"
288c2ecf20Sopenharmony_ci#include <asm/dcr-regs.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int mal_count;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciint mal_register_commac(struct mal_instance *mal, struct mal_commac *commac)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	unsigned long flags;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	MAL_DBG(mal, "reg(%08x, %08x)" NL,
398c2ecf20Sopenharmony_ci		commac->tx_chan_mask, commac->rx_chan_mask);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Don't let multiple commacs claim the same channel(s) */
428c2ecf20Sopenharmony_ci	if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
438c2ecf20Sopenharmony_ci	    (mal->rx_chan_mask & commac->rx_chan_mask)) {
448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mal->lock, flags);
458c2ecf20Sopenharmony_ci		printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
468c2ecf20Sopenharmony_ci		       mal->index);
478c2ecf20Sopenharmony_ci		return -EBUSY;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (list_empty(&mal->list))
518c2ecf20Sopenharmony_ci		napi_enable(&mal->napi);
528c2ecf20Sopenharmony_ci	mal->tx_chan_mask |= commac->tx_chan_mask;
538c2ecf20Sopenharmony_ci	mal->rx_chan_mask |= commac->rx_chan_mask;
548c2ecf20Sopenharmony_ci	list_add(&commac->list, &mal->list);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid mal_unregister_commac(struct mal_instance	*mal,
628c2ecf20Sopenharmony_ci		struct mal_commac *commac)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned long flags;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	MAL_DBG(mal, "unreg(%08x, %08x)" NL,
698c2ecf20Sopenharmony_ci		commac->tx_chan_mask, commac->rx_chan_mask);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	mal->tx_chan_mask &= ~commac->tx_chan_mask;
728c2ecf20Sopenharmony_ci	mal->rx_chan_mask &= ~commac->rx_chan_mask;
738c2ecf20Sopenharmony_ci	list_del_init(&commac->list);
748c2ecf20Sopenharmony_ci	if (list_empty(&mal->list))
758c2ecf20Sopenharmony_ci		napi_disable(&mal->napi);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciint mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_rx_chans ||
838c2ecf20Sopenharmony_ci	       size > MAL_MAX_RX_SIZE);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	MAL_DBG(mal, "set_rbcs(%d, %lu)" NL, channel, size);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (size & 0xf) {
888c2ecf20Sopenharmony_ci		printk(KERN_WARNING
898c2ecf20Sopenharmony_ci		       "mal%d: incorrect RX size %lu for the channel %d\n",
908c2ecf20Sopenharmony_ci		       mal->index, size, channel);
918c2ecf20Sopenharmony_ci		return -EINVAL;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_RCBS(channel), size >> 4);
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciint mal_tx_bd_offset(struct mal_instance *mal, int channel)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_tx_chans);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return channel * NUM_TX_BUFF;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ciint mal_rx_bd_offset(struct mal_instance *mal, int channel)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	BUG_ON(channel < 0 || channel >= mal->num_rx_chans);
1088c2ecf20Sopenharmony_ci	return mal->num_tx_chans * NUM_TX_BUFF + channel * NUM_RX_BUFF;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_civoid mal_enable_tx_channel(struct mal_instance *mal, int channel)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	unsigned long flags;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	MAL_DBG(mal, "enable_tx(%d)" NL, channel);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXCASR,
1208c2ecf20Sopenharmony_ci		     get_mal_dcrn(mal, MAL_TXCASR) | MAL_CHAN_MASK(channel));
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_civoid mal_disable_tx_channel(struct mal_instance *mal, int channel)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXCARR, MAL_CHAN_MASK(channel));
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	MAL_DBG(mal, "disable_tx(%d)" NL, channel);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_civoid mal_enable_rx_channel(struct mal_instance *mal, int channel)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned long flags;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple
1388c2ecf20Sopenharmony_ci	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value
1398c2ecf20Sopenharmony_ci	 * for the bitmask
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	if (!(channel % 8))
1428c2ecf20Sopenharmony_ci		channel >>= 3;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	MAL_DBG(mal, "enable_rx(%d)" NL, channel);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXCASR,
1498c2ecf20Sopenharmony_ci		     get_mal_dcrn(mal, MAL_RXCASR) | MAL_CHAN_MASK(channel));
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_civoid mal_disable_rx_channel(struct mal_instance *mal, int channel)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	/*
1578c2ecf20Sopenharmony_ci	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple
1588c2ecf20Sopenharmony_ci	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value
1598c2ecf20Sopenharmony_ci	 * for the bitmask
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	if (!(channel % 8))
1628c2ecf20Sopenharmony_ci		channel >>= 3;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel));
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	MAL_DBG(mal, "disable_rx(%d)" NL, channel);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_civoid mal_poll_add(struct mal_instance *mal, struct mal_commac *commac)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	unsigned long flags;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	MAL_DBG(mal, "poll_add(%p)" NL, commac);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* starts disabled */
1788c2ecf20Sopenharmony_ci	set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	list_add_tail(&commac->poll_list, &mal->poll_list);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_civoid mal_poll_del(struct mal_instance *mal, struct mal_commac *commac)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	unsigned long flags;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mal->lock, flags);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	MAL_DBG(mal, "poll_del(%p)" NL, commac);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	list_del(&commac->poll_list);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mal->lock, flags);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/* synchronized by mal_poll() */
1998c2ecf20Sopenharmony_cistatic inline void mal_enable_eob_irq(struct mal_instance *mal)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "enable_irq" NL);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
2048c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) | MAL_CFG_EOPIE);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* synchronized by NAPI state */
2088c2ecf20Sopenharmony_cistatic inline void mal_disable_eob_irq(struct mal_instance *mal)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
2118c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) & ~MAL_CFG_EOPIE);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "disable_irq" NL);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic irqreturn_t mal_serr(int irq, void *dev_instance)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	u32 esr = get_mal_dcrn(mal, MAL_ESR);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* Clear the error status register */
2238c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_ESR, esr);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	MAL_DBG(mal, "SERR %08x" NL, esr);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (esr & MAL_ESR_EVB) {
2288c2ecf20Sopenharmony_ci		if (esr & MAL_ESR_DE) {
2298c2ecf20Sopenharmony_ci			/* We ignore Descriptor error,
2308c2ecf20Sopenharmony_ci			 * TXDE or RXDE interrupt will be generated anyway.
2318c2ecf20Sopenharmony_ci			 */
2328c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (esr & MAL_ESR_PEIN) {
2368c2ecf20Sopenharmony_ci			/* PLB error, it's probably buggy hardware or
2378c2ecf20Sopenharmony_ci			 * incorrect physical address in BD (i.e. bug)
2388c2ecf20Sopenharmony_ci			 */
2398c2ecf20Sopenharmony_ci			if (net_ratelimit())
2408c2ecf20Sopenharmony_ci				printk(KERN_ERR
2418c2ecf20Sopenharmony_ci				       "mal%d: system error, "
2428c2ecf20Sopenharmony_ci				       "PLB (ESR = 0x%08x)\n",
2438c2ecf20Sopenharmony_ci				       mal->index, esr);
2448c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		/* OPB error, it's probably buggy hardware or incorrect
2488c2ecf20Sopenharmony_ci		 * EBC setup
2498c2ecf20Sopenharmony_ci		 */
2508c2ecf20Sopenharmony_ci		if (net_ratelimit())
2518c2ecf20Sopenharmony_ci			printk(KERN_ERR
2528c2ecf20Sopenharmony_ci			       "mal%d: system error, OPB (ESR = 0x%08x)\n",
2538c2ecf20Sopenharmony_ci			       mal->index, esr);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic inline void mal_schedule_poll(struct mal_instance *mal)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	if (likely(napi_schedule_prep(&mal->napi))) {
2618c2ecf20Sopenharmony_ci		MAL_DBG2(mal, "schedule_poll" NL);
2628c2ecf20Sopenharmony_ci		spin_lock(&mal->lock);
2638c2ecf20Sopenharmony_ci		mal_disable_eob_irq(mal);
2648c2ecf20Sopenharmony_ci		spin_unlock(&mal->lock);
2658c2ecf20Sopenharmony_ci		__napi_schedule(&mal->napi);
2668c2ecf20Sopenharmony_ci	} else
2678c2ecf20Sopenharmony_ci		MAL_DBG2(mal, "already in poll" NL);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic irqreturn_t mal_txeob(int irq, void *dev_instance)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	u32 r = get_mal_dcrn(mal, MAL_TXEOBISR);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "txeob %08x" NL, r);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	mal_schedule_poll(mal);
2798c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXEOBISR, r);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR_NATIVE
2828c2ecf20Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
2838c2ecf20Sopenharmony_ci		mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
2848c2ecf20Sopenharmony_ci				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
2858c2ecf20Sopenharmony_ci#endif
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic irqreturn_t mal_rxeob(int irq, void *dev_instance)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	u32 r = get_mal_dcrn(mal, MAL_RXEOBISR);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "rxeob %08x" NL, r);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	mal_schedule_poll(mal);
2998c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXEOBISR, r);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_DCR_NATIVE
3028c2ecf20Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
3038c2ecf20Sopenharmony_ci		mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
3048c2ecf20Sopenharmony_ci				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
3058c2ecf20Sopenharmony_ci#endif
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic irqreturn_t mal_txde(int irq, void *dev_instance)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	u32 deir = get_mal_dcrn(mal, MAL_TXDEIR);
3158c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_TXDEIR, deir);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	MAL_DBG(mal, "txde %08x" NL, deir);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (net_ratelimit())
3208c2ecf20Sopenharmony_ci		printk(KERN_ERR
3218c2ecf20Sopenharmony_ci		       "mal%d: TX descriptor error (TXDEIR = 0x%08x)\n",
3228c2ecf20Sopenharmony_ci		       mal->index, deir);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic irqreturn_t mal_rxde(int irq, void *dev_instance)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
3308c2ecf20Sopenharmony_ci	struct list_head *l;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	u32 deir = get_mal_dcrn(mal, MAL_RXDEIR);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	MAL_DBG(mal, "rxde %08x" NL, deir);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	list_for_each(l, &mal->list) {
3378c2ecf20Sopenharmony_ci		struct mal_commac *mc = list_entry(l, struct mal_commac, list);
3388c2ecf20Sopenharmony_ci		if (deir & mc->rx_chan_mask) {
3398c2ecf20Sopenharmony_ci			set_bit(MAL_COMMAC_RX_STOPPED, &mc->flags);
3408c2ecf20Sopenharmony_ci			mc->ops->rxde(mc->dev);
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	mal_schedule_poll(mal);
3458c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_RXDEIR, deir);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic irqreturn_t mal_int(int irq, void *dev_instance)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct mal_instance *mal = dev_instance;
3538c2ecf20Sopenharmony_ci	u32 esr = get_mal_dcrn(mal, MAL_ESR);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (esr & MAL_ESR_EVB) {
3568c2ecf20Sopenharmony_ci		/* descriptor error */
3578c2ecf20Sopenharmony_ci		if (esr & MAL_ESR_DE) {
3588c2ecf20Sopenharmony_ci			if (esr & MAL_ESR_CIDT)
3598c2ecf20Sopenharmony_ci				return mal_rxde(irq, dev_instance);
3608c2ecf20Sopenharmony_ci			else
3618c2ecf20Sopenharmony_ci				return mal_txde(irq, dev_instance);
3628c2ecf20Sopenharmony_ci		} else { /* SERR */
3638c2ecf20Sopenharmony_ci			return mal_serr(irq, dev_instance);
3648c2ecf20Sopenharmony_ci		}
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_civoid mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	/* Spinlock-type semantics: only one caller disable poll at a time */
3728c2ecf20Sopenharmony_ci	while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
3738c2ecf20Sopenharmony_ci		msleep(1);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/* Synchronize with the MAL NAPI poller */
3768c2ecf20Sopenharmony_ci	napi_synchronize(&mal->napi);
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_civoid mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	smp_wmb();
3828c2ecf20Sopenharmony_ci	clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Feels better to trigger a poll here to catch up with events that
3858c2ecf20Sopenharmony_ci	 * may have happened on this channel while disabled. It will most
3868c2ecf20Sopenharmony_ci	 * probably be delayed until the next interrupt but that's mostly a
3878c2ecf20Sopenharmony_ci	 * non-issue in the context where this is called.
3888c2ecf20Sopenharmony_ci	 */
3898c2ecf20Sopenharmony_ci	napi_schedule(&mal->napi);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int mal_poll(struct napi_struct *napi, int budget)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct mal_instance *mal = container_of(napi, struct mal_instance, napi);
3958c2ecf20Sopenharmony_ci	struct list_head *l;
3968c2ecf20Sopenharmony_ci	int received = 0;
3978c2ecf20Sopenharmony_ci	unsigned long flags;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "poll(%d)" NL, budget);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* Process TX skbs */
4028c2ecf20Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
4038c2ecf20Sopenharmony_ci		struct mal_commac *mc =
4048c2ecf20Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
4058c2ecf20Sopenharmony_ci		mc->ops->poll_tx(mc->dev);
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Process RX skbs.
4098c2ecf20Sopenharmony_ci	 *
4108c2ecf20Sopenharmony_ci	 * We _might_ need something more smart here to enforce polling
4118c2ecf20Sopenharmony_ci	 * fairness.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
4148c2ecf20Sopenharmony_ci		struct mal_commac *mc =
4158c2ecf20Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
4168c2ecf20Sopenharmony_ci		int n;
4178c2ecf20Sopenharmony_ci		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
4188c2ecf20Sopenharmony_ci			continue;
4198c2ecf20Sopenharmony_ci		n = mc->ops->poll_rx(mc->dev, budget - received);
4208c2ecf20Sopenharmony_ci		if (n) {
4218c2ecf20Sopenharmony_ci			received += n;
4228c2ecf20Sopenharmony_ci			if (received >= budget)
4238c2ecf20Sopenharmony_ci				return budget;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (napi_complete_done(napi, received)) {
4288c2ecf20Sopenharmony_ci		/* We need to disable IRQs to protect from RXDE IRQ here */
4298c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mal->lock, flags);
4308c2ecf20Sopenharmony_ci		mal_enable_eob_irq(mal);
4318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mal->lock, flags);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Check for "rotting" packet(s) */
4358c2ecf20Sopenharmony_ci	list_for_each(l, &mal->poll_list) {
4368c2ecf20Sopenharmony_ci		struct mal_commac *mc =
4378c2ecf20Sopenharmony_ci			list_entry(l, struct mal_commac, poll_list);
4388c2ecf20Sopenharmony_ci		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
4398c2ecf20Sopenharmony_ci			continue;
4408c2ecf20Sopenharmony_ci		if (unlikely(mc->ops->peek_rx(mc->dev) ||
4418c2ecf20Sopenharmony_ci			     test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) {
4428c2ecf20Sopenharmony_ci			MAL_DBG2(mal, "rotting packet" NL);
4438c2ecf20Sopenharmony_ci			if (!napi_reschedule(napi))
4448c2ecf20Sopenharmony_ci				goto more_work;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci			spin_lock_irqsave(&mal->lock, flags);
4478c2ecf20Sopenharmony_ci			mal_disable_eob_irq(mal);
4488c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&mal->lock, flags);
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci		mc->ops->poll_tx(mc->dev);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci more_work:
4548c2ecf20Sopenharmony_ci	MAL_DBG2(mal, "poll() %d <- %d" NL, budget, received);
4558c2ecf20Sopenharmony_ci	return received;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void mal_reset(struct mal_instance *mal)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	int n = 10;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	MAL_DBG(mal, "reset" NL);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, MAL_CFG_SR);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* Wait for reset to complete (1 system clock) */
4678c2ecf20Sopenharmony_ci	while ((get_mal_dcrn(mal, MAL_CFG) & MAL_CFG_SR) && n)
4688c2ecf20Sopenharmony_ci		--n;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (unlikely(!n))
4718c2ecf20Sopenharmony_ci		printk(KERN_ERR "mal%d: reset timeout\n", mal->index);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ciint mal_get_regs_len(struct mal_instance *mal)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	return sizeof(struct emac_ethtool_regs_subhdr) +
4778c2ecf20Sopenharmony_ci	    sizeof(struct mal_regs);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_civoid *mal_dump_regs(struct mal_instance *mal, void *buf)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct emac_ethtool_regs_subhdr *hdr = buf;
4838c2ecf20Sopenharmony_ci	struct mal_regs *regs = (struct mal_regs *)(hdr + 1);
4848c2ecf20Sopenharmony_ci	int i;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	hdr->version = mal->version;
4878c2ecf20Sopenharmony_ci	hdr->index = mal->index;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	regs->tx_count = mal->num_tx_chans;
4908c2ecf20Sopenharmony_ci	regs->rx_count = mal->num_rx_chans;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	regs->cfg = get_mal_dcrn(mal, MAL_CFG);
4938c2ecf20Sopenharmony_ci	regs->esr = get_mal_dcrn(mal, MAL_ESR);
4948c2ecf20Sopenharmony_ci	regs->ier = get_mal_dcrn(mal, MAL_IER);
4958c2ecf20Sopenharmony_ci	regs->tx_casr = get_mal_dcrn(mal, MAL_TXCASR);
4968c2ecf20Sopenharmony_ci	regs->tx_carr = get_mal_dcrn(mal, MAL_TXCARR);
4978c2ecf20Sopenharmony_ci	regs->tx_eobisr = get_mal_dcrn(mal, MAL_TXEOBISR);
4988c2ecf20Sopenharmony_ci	regs->tx_deir = get_mal_dcrn(mal, MAL_TXDEIR);
4998c2ecf20Sopenharmony_ci	regs->rx_casr = get_mal_dcrn(mal, MAL_RXCASR);
5008c2ecf20Sopenharmony_ci	regs->rx_carr = get_mal_dcrn(mal, MAL_RXCARR);
5018c2ecf20Sopenharmony_ci	regs->rx_eobisr = get_mal_dcrn(mal, MAL_RXEOBISR);
5028c2ecf20Sopenharmony_ci	regs->rx_deir = get_mal_dcrn(mal, MAL_RXDEIR);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	for (i = 0; i < regs->tx_count; ++i)
5058c2ecf20Sopenharmony_ci		regs->tx_ctpr[i] = get_mal_dcrn(mal, MAL_TXCTPR(i));
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	for (i = 0; i < regs->rx_count; ++i) {
5088c2ecf20Sopenharmony_ci		regs->rx_ctpr[i] = get_mal_dcrn(mal, MAL_RXCTPR(i));
5098c2ecf20Sopenharmony_ci		regs->rcbs[i] = get_mal_dcrn(mal, MAL_RCBS(i));
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci	return regs + 1;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic int mal_probe(struct platform_device *ofdev)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct mal_instance *mal;
5178c2ecf20Sopenharmony_ci	int err = 0, i, bd_size;
5188c2ecf20Sopenharmony_ci	int index = mal_count++;
5198c2ecf20Sopenharmony_ci	unsigned int dcr_base;
5208c2ecf20Sopenharmony_ci	const u32 *prop;
5218c2ecf20Sopenharmony_ci	u32 cfg;
5228c2ecf20Sopenharmony_ci	unsigned long irqflags;
5238c2ecf20Sopenharmony_ci	irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	mal = kzalloc(sizeof(struct mal_instance), GFP_KERNEL);
5268c2ecf20Sopenharmony_ci	if (!mal)
5278c2ecf20Sopenharmony_ci		return -ENOMEM;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	mal->index = index;
5308c2ecf20Sopenharmony_ci	mal->ofdev = ofdev;
5318c2ecf20Sopenharmony_ci	mal->version = of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal2") ? 2 : 1;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	MAL_DBG(mal, "probe" NL);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	prop = of_get_property(ofdev->dev.of_node, "num-tx-chans", NULL);
5368c2ecf20Sopenharmony_ci	if (prop == NULL) {
5378c2ecf20Sopenharmony_ci		printk(KERN_ERR
5388c2ecf20Sopenharmony_ci		       "mal%d: can't find MAL num-tx-chans property!\n",
5398c2ecf20Sopenharmony_ci		       index);
5408c2ecf20Sopenharmony_ci		err = -ENODEV;
5418c2ecf20Sopenharmony_ci		goto fail;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	mal->num_tx_chans = prop[0];
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL);
5468c2ecf20Sopenharmony_ci	if (prop == NULL) {
5478c2ecf20Sopenharmony_ci		printk(KERN_ERR
5488c2ecf20Sopenharmony_ci		       "mal%d: can't find MAL num-rx-chans property!\n",
5498c2ecf20Sopenharmony_ci		       index);
5508c2ecf20Sopenharmony_ci		err = -ENODEV;
5518c2ecf20Sopenharmony_ci		goto fail;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci	mal->num_rx_chans = prop[0];
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	dcr_base = dcr_resource_start(ofdev->dev.of_node, 0);
5568c2ecf20Sopenharmony_ci	if (dcr_base == 0) {
5578c2ecf20Sopenharmony_ci		printk(KERN_ERR
5588c2ecf20Sopenharmony_ci		       "mal%d: can't find DCR resource!\n", index);
5598c2ecf20Sopenharmony_ci		err = -ENODEV;
5608c2ecf20Sopenharmony_ci		goto fail;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	mal->dcr_host = dcr_map(ofdev->dev.of_node, dcr_base, 0x100);
5638c2ecf20Sopenharmony_ci	if (!DCR_MAP_OK(mal->dcr_host)) {
5648c2ecf20Sopenharmony_ci		printk(KERN_ERR
5658c2ecf20Sopenharmony_ci		       "mal%d: failed to map DCRs !\n", index);
5668c2ecf20Sopenharmony_ci		err = -ENODEV;
5678c2ecf20Sopenharmony_ci		goto fail;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-405ez")) {
5718c2ecf20Sopenharmony_ci#if defined(CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT) && \
5728c2ecf20Sopenharmony_ci		defined(CONFIG_IBM_EMAC_MAL_COMMON_ERR)
5738c2ecf20Sopenharmony_ci		mal->features |= (MAL_FTR_CLEAR_ICINTSTAT |
5748c2ecf20Sopenharmony_ci				MAL_FTR_COMMON_ERR_INT);
5758c2ecf20Sopenharmony_ci#else
5768c2ecf20Sopenharmony_ci		printk(KERN_ERR "%pOF: Support for 405EZ not enabled!\n",
5778c2ecf20Sopenharmony_ci				ofdev->dev.of_node);
5788c2ecf20Sopenharmony_ci		err = -ENODEV;
5798c2ecf20Sopenharmony_ci		goto fail;
5808c2ecf20Sopenharmony_ci#endif
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	mal->txeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
5848c2ecf20Sopenharmony_ci	mal->rxeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 1);
5858c2ecf20Sopenharmony_ci	mal->serr_irq = irq_of_parse_and_map(ofdev->dev.of_node, 2);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) {
5888c2ecf20Sopenharmony_ci		mal->txde_irq = mal->rxde_irq = mal->serr_irq;
5898c2ecf20Sopenharmony_ci	} else {
5908c2ecf20Sopenharmony_ci		mal->txde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 3);
5918c2ecf20Sopenharmony_ci		mal->rxde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 4);
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (!mal->txeob_irq || !mal->rxeob_irq || !mal->serr_irq ||
5958c2ecf20Sopenharmony_ci	    !mal->txde_irq  || !mal->rxde_irq) {
5968c2ecf20Sopenharmony_ci		printk(KERN_ERR
5978c2ecf20Sopenharmony_ci		       "mal%d: failed to map interrupts !\n", index);
5988c2ecf20Sopenharmony_ci		err = -ENODEV;
5998c2ecf20Sopenharmony_ci		goto fail_unmap;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mal->poll_list);
6038c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mal->list);
6048c2ecf20Sopenharmony_ci	spin_lock_init(&mal->lock);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	init_dummy_netdev(&mal->dummy_dev);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	netif_napi_add(&mal->dummy_dev, &mal->napi, mal_poll,
6098c2ecf20Sopenharmony_ci		       CONFIG_IBM_EMAC_POLL_WEIGHT);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* Load power-on reset defaults */
6128c2ecf20Sopenharmony_ci	mal_reset(mal);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* Set the MAL configuration register */
6158c2ecf20Sopenharmony_ci	cfg = (mal->version == 2) ? MAL2_CFG_DEFAULT : MAL1_CFG_DEFAULT;
6168c2ecf20Sopenharmony_ci	cfg |= MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	/* Current Axon is not happy with priority being non-0, it can
6198c2ecf20Sopenharmony_ci	 * deadlock, fix it up here
6208c2ecf20Sopenharmony_ci	 */
6218c2ecf20Sopenharmony_ci	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-axon"))
6228c2ecf20Sopenharmony_ci		cfg &= ~(MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* Apply configuration */
6258c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_CFG, cfg);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* Allocate space for BD rings */
6288c2ecf20Sopenharmony_ci	BUG_ON(mal->num_tx_chans <= 0 || mal->num_tx_chans > 32);
6298c2ecf20Sopenharmony_ci	BUG_ON(mal->num_rx_chans <= 0 || mal->num_rx_chans > 32);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	bd_size = sizeof(struct mal_descriptor) *
6328c2ecf20Sopenharmony_ci		(NUM_TX_BUFF * mal->num_tx_chans +
6338c2ecf20Sopenharmony_ci		 NUM_RX_BUFF * mal->num_rx_chans);
6348c2ecf20Sopenharmony_ci	mal->bd_virt = dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma,
6358c2ecf20Sopenharmony_ci					  GFP_KERNEL);
6368c2ecf20Sopenharmony_ci	if (mal->bd_virt == NULL) {
6378c2ecf20Sopenharmony_ci		err = -ENOMEM;
6388c2ecf20Sopenharmony_ci		goto fail_unmap;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	for (i = 0; i < mal->num_tx_chans; ++i)
6428c2ecf20Sopenharmony_ci		set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma +
6438c2ecf20Sopenharmony_ci			     sizeof(struct mal_descriptor) *
6448c2ecf20Sopenharmony_ci			     mal_tx_bd_offset(mal, i));
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	for (i = 0; i < mal->num_rx_chans; ++i)
6478c2ecf20Sopenharmony_ci		set_mal_dcrn(mal, MAL_RXCTPR(i), mal->bd_dma +
6488c2ecf20Sopenharmony_ci			     sizeof(struct mal_descriptor) *
6498c2ecf20Sopenharmony_ci			     mal_rx_bd_offset(mal, i));
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) {
6528c2ecf20Sopenharmony_ci		irqflags = IRQF_SHARED;
6538c2ecf20Sopenharmony_ci		hdlr_serr = hdlr_txde = hdlr_rxde = mal_int;
6548c2ecf20Sopenharmony_ci	} else {
6558c2ecf20Sopenharmony_ci		irqflags = 0;
6568c2ecf20Sopenharmony_ci		hdlr_serr = mal_serr;
6578c2ecf20Sopenharmony_ci		hdlr_txde = mal_txde;
6588c2ecf20Sopenharmony_ci		hdlr_rxde = mal_rxde;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	err = request_irq(mal->serr_irq, hdlr_serr, irqflags, "MAL SERR", mal);
6628c2ecf20Sopenharmony_ci	if (err)
6638c2ecf20Sopenharmony_ci		goto fail2;
6648c2ecf20Sopenharmony_ci	err = request_irq(mal->txde_irq, hdlr_txde, irqflags, "MAL TX DE", mal);
6658c2ecf20Sopenharmony_ci	if (err)
6668c2ecf20Sopenharmony_ci		goto fail3;
6678c2ecf20Sopenharmony_ci	err = request_irq(mal->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal);
6688c2ecf20Sopenharmony_ci	if (err)
6698c2ecf20Sopenharmony_ci		goto fail4;
6708c2ecf20Sopenharmony_ci	err = request_irq(mal->rxde_irq, hdlr_rxde, irqflags, "MAL RX DE", mal);
6718c2ecf20Sopenharmony_ci	if (err)
6728c2ecf20Sopenharmony_ci		goto fail5;
6738c2ecf20Sopenharmony_ci	err = request_irq(mal->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal);
6748c2ecf20Sopenharmony_ci	if (err)
6758c2ecf20Sopenharmony_ci		goto fail6;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Enable all MAL SERR interrupt sources */
6788c2ecf20Sopenharmony_ci	set_mal_dcrn(mal, MAL_IER, MAL_IER_EVENTS);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Enable EOB interrupt */
6818c2ecf20Sopenharmony_ci	mal_enable_eob_irq(mal);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	printk(KERN_INFO
6848c2ecf20Sopenharmony_ci	       "MAL v%d %pOF, %d TX channels, %d RX channels\n",
6858c2ecf20Sopenharmony_ci	       mal->version, ofdev->dev.of_node,
6868c2ecf20Sopenharmony_ci	       mal->num_tx_chans, mal->num_rx_chans);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* Advertise this instance to the rest of the world */
6898c2ecf20Sopenharmony_ci	wmb();
6908c2ecf20Sopenharmony_ci	platform_set_drvdata(ofdev, mal);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci fail6:
6958c2ecf20Sopenharmony_ci	free_irq(mal->rxde_irq, mal);
6968c2ecf20Sopenharmony_ci fail5:
6978c2ecf20Sopenharmony_ci	free_irq(mal->txeob_irq, mal);
6988c2ecf20Sopenharmony_ci fail4:
6998c2ecf20Sopenharmony_ci	free_irq(mal->txde_irq, mal);
7008c2ecf20Sopenharmony_ci fail3:
7018c2ecf20Sopenharmony_ci	free_irq(mal->serr_irq, mal);
7028c2ecf20Sopenharmony_ci fail2:
7038c2ecf20Sopenharmony_ci	dma_free_coherent(&ofdev->dev, bd_size, mal->bd_virt, mal->bd_dma);
7048c2ecf20Sopenharmony_ci fail_unmap:
7058c2ecf20Sopenharmony_ci	dcr_unmap(mal->dcr_host, 0x100);
7068c2ecf20Sopenharmony_ci fail:
7078c2ecf20Sopenharmony_ci	kfree(mal);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return err;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic int mal_remove(struct platform_device *ofdev)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct mal_instance *mal = platform_get_drvdata(ofdev);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	MAL_DBG(mal, "remove" NL);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* Synchronize with scheduled polling */
7198c2ecf20Sopenharmony_ci	napi_disable(&mal->napi);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (!list_empty(&mal->list))
7228c2ecf20Sopenharmony_ci		/* This is *very* bad */
7238c2ecf20Sopenharmony_ci		WARN(1, KERN_EMERG
7248c2ecf20Sopenharmony_ci		       "mal%d: commac list is not empty on remove!\n",
7258c2ecf20Sopenharmony_ci		       mal->index);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	free_irq(mal->serr_irq, mal);
7288c2ecf20Sopenharmony_ci	free_irq(mal->txde_irq, mal);
7298c2ecf20Sopenharmony_ci	free_irq(mal->txeob_irq, mal);
7308c2ecf20Sopenharmony_ci	free_irq(mal->rxde_irq, mal);
7318c2ecf20Sopenharmony_ci	free_irq(mal->rxeob_irq, mal);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	mal_reset(mal);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	dma_free_coherent(&ofdev->dev,
7368c2ecf20Sopenharmony_ci			  sizeof(struct mal_descriptor) *
7378c2ecf20Sopenharmony_ci			  (NUM_TX_BUFF * mal->num_tx_chans +
7388c2ecf20Sopenharmony_ci			   NUM_RX_BUFF * mal->num_rx_chans), mal->bd_virt,
7398c2ecf20Sopenharmony_ci			  mal->bd_dma);
7408c2ecf20Sopenharmony_ci	kfree(mal);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	return 0;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic const struct of_device_id mal_platform_match[] =
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	{
7488c2ecf20Sopenharmony_ci		.compatible	= "ibm,mcmal",
7498c2ecf20Sopenharmony_ci	},
7508c2ecf20Sopenharmony_ci	{
7518c2ecf20Sopenharmony_ci		.compatible	= "ibm,mcmal2",
7528c2ecf20Sopenharmony_ci	},
7538c2ecf20Sopenharmony_ci	/* Backward compat */
7548c2ecf20Sopenharmony_ci	{
7558c2ecf20Sopenharmony_ci		.type		= "mcmal-dma",
7568c2ecf20Sopenharmony_ci		.compatible	= "ibm,mcmal",
7578c2ecf20Sopenharmony_ci	},
7588c2ecf20Sopenharmony_ci	{
7598c2ecf20Sopenharmony_ci		.type		= "mcmal-dma",
7608c2ecf20Sopenharmony_ci		.compatible	= "ibm,mcmal2",
7618c2ecf20Sopenharmony_ci	},
7628c2ecf20Sopenharmony_ci	{},
7638c2ecf20Sopenharmony_ci};
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic struct platform_driver mal_of_driver = {
7668c2ecf20Sopenharmony_ci	.driver = {
7678c2ecf20Sopenharmony_ci		.name = "mcmal",
7688c2ecf20Sopenharmony_ci		.of_match_table = mal_platform_match,
7698c2ecf20Sopenharmony_ci	},
7708c2ecf20Sopenharmony_ci	.probe = mal_probe,
7718c2ecf20Sopenharmony_ci	.remove = mal_remove,
7728c2ecf20Sopenharmony_ci};
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ciint __init mal_init(void)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	return platform_driver_register(&mal_of_driver);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_civoid mal_exit(void)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	platform_driver_unregister(&mal_of_driver);
7828c2ecf20Sopenharmony_ci}
783