mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-31 08:08:51 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										76
									
								
								drivers/net/ethernet/ibm/emac/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								drivers/net/ethernet/ibm/emac/Kconfig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| config IBM_EMAC | ||||
| 	tristate "IBM EMAC Ethernet support" | ||||
| 	depends on PPC_DCR | ||||
| 	select CRC32 | ||||
| 	help | ||||
| 	  This driver supports the IBM EMAC family of Ethernet controllers | ||||
| 	  typically found on 4xx embedded PowerPC chips, but also on the | ||||
| 	  Axon southbridge for Cell. | ||||
| 
 | ||||
| config IBM_EMAC_RXB | ||||
| 	int "Number of receive buffers" | ||||
| 	depends on IBM_EMAC | ||||
| 	default "128" | ||||
| 
 | ||||
| config IBM_EMAC_TXB | ||||
| 	int "Number of transmit buffers" | ||||
| 	depends on IBM_EMAC | ||||
| 	default "64" | ||||
| 
 | ||||
| config IBM_EMAC_POLL_WEIGHT | ||||
| 	int "MAL NAPI polling weight" | ||||
| 	depends on IBM_EMAC | ||||
| 	default "32" | ||||
| 
 | ||||
| config IBM_EMAC_RX_COPY_THRESHOLD | ||||
| 	int "RX skb copy threshold (bytes)" | ||||
| 	depends on IBM_EMAC | ||||
| 	default "256" | ||||
| 
 | ||||
| config IBM_EMAC_RX_SKB_HEADROOM | ||||
| 	int "Additional RX skb headroom (bytes)" | ||||
| 	depends on IBM_EMAC | ||||
| 	default "0" | ||||
| 	help | ||||
| 	  Additional receive skb headroom. Note, that driver | ||||
| 	  will always reserve at least 2 bytes to make IP header | ||||
| 	  aligned, so usually there is no need to add any additional | ||||
| 	  headroom. | ||||
| 
 | ||||
| 	  If unsure, set to 0. | ||||
| 
 | ||||
| config IBM_EMAC_DEBUG | ||||
| 	bool "Debugging" | ||||
| 	depends on IBM_EMAC | ||||
| 	default n | ||||
| 
 | ||||
| # The options below has to be select'ed by the respective | ||||
| # processor types or platforms | ||||
| 
 | ||||
| config IBM_EMAC_ZMII | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_RGMII | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_TAH | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_EMAC4 | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_NO_FLOW_CTRL | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_MAL_CLR_ICINTSTAT | ||||
| 	bool | ||||
| 	default n | ||||
| 
 | ||||
| config IBM_EMAC_MAL_COMMON_ERR | ||||
| 	bool | ||||
| 	default n | ||||
							
								
								
									
										11
									
								
								drivers/net/ethernet/ibm/emac/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								drivers/net/ethernet/ibm/emac/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| #
 | ||||
| # Makefile for the PowerPC 4xx on-chip ethernet driver
 | ||||
| #
 | ||||
| 
 | ||||
| obj-$(CONFIG_IBM_EMAC) += ibm_emac.o | ||||
| 
 | ||||
| ibm_emac-y := mal.o core.o phy.o | ||||
| ibm_emac-$(CONFIG_IBM_EMAC_ZMII) += zmii.o | ||||
| ibm_emac-$(CONFIG_IBM_EMAC_RGMII) += rgmii.o | ||||
| ibm_emac-$(CONFIG_IBM_EMAC_TAH) += tah.o | ||||
| ibm_emac-$(CONFIG_IBM_EMAC_DEBUG) += debug.o | ||||
							
								
								
									
										3121
									
								
								drivers/net/ethernet/ibm/emac/core.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3121
									
								
								drivers/net/ethernet/ibm/emac/core.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										470
									
								
								drivers/net/ethernet/ibm/emac/core.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								drivers/net/ethernet/ibm/emac/core.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,470 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/core.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  * 	Johnnie Peters <jpeters@mvista.com> | ||||
|  *      Copyright 2000, 2001 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. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __IBM_NEWEMAC_CORE_H | ||||
| #define __IBM_NEWEMAC_CORE_H | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/of_platform.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <asm/io.h> | ||||
| #include <asm/dcr.h> | ||||
| 
 | ||||
| #include "emac.h" | ||||
| #include "phy.h" | ||||
| #include "zmii.h" | ||||
| #include "rgmii.h" | ||||
| #include "mal.h" | ||||
| #include "tah.h" | ||||
| #include "debug.h" | ||||
| 
 | ||||
| #define NUM_TX_BUFF			CONFIG_IBM_EMAC_TXB | ||||
| #define NUM_RX_BUFF			CONFIG_IBM_EMAC_RXB | ||||
| 
 | ||||
| /* Simple sanity check */ | ||||
| #if NUM_TX_BUFF > 256 || NUM_RX_BUFF > 256 | ||||
| #error Invalid number of buffer descriptors (greater than 256) | ||||
| #endif | ||||
| 
 | ||||
| #define EMAC_MIN_MTU			46 | ||||
| 
 | ||||
| /* Maximum L2 header length (VLAN tagged, no FCS) */ | ||||
| #define EMAC_MTU_OVERHEAD		(6 * 2 + 2 + 4) | ||||
| 
 | ||||
| /* RX BD size for the given MTU */ | ||||
| static inline int emac_rx_size(int mtu) | ||||
| { | ||||
| 	if (mtu > ETH_DATA_LEN) | ||||
| 		return MAL_MAX_RX_SIZE; | ||||
| 	else | ||||
| 		return mal_rx_size(ETH_DATA_LEN + EMAC_MTU_OVERHEAD); | ||||
| } | ||||
| 
 | ||||
| #define EMAC_DMA_ALIGN(x)		ALIGN((x), dma_get_cache_alignment()) | ||||
| 
 | ||||
| #define EMAC_RX_SKB_HEADROOM		\ | ||||
| 	EMAC_DMA_ALIGN(CONFIG_IBM_EMAC_RX_SKB_HEADROOM) | ||||
| 
 | ||||
| /* Size of RX skb for the given MTU */ | ||||
| static inline int emac_rx_skb_size(int mtu) | ||||
| { | ||||
| 	int size = max(mtu + EMAC_MTU_OVERHEAD, emac_rx_size(mtu)); | ||||
| 	return EMAC_DMA_ALIGN(size + 2) + EMAC_RX_SKB_HEADROOM; | ||||
| } | ||||
| 
 | ||||
| /* RX DMA sync size */ | ||||
| static inline int emac_rx_sync_size(int mtu) | ||||
| { | ||||
| 	return EMAC_DMA_ALIGN(emac_rx_size(mtu) + 2); | ||||
| } | ||||
| 
 | ||||
| /* Driver statistcs is split into two parts to make it more cache friendly:
 | ||||
|  *   - normal statistics (packet count, etc) | ||||
|  *   - error statistics | ||||
|  * | ||||
|  * When statistics is requested by ethtool, these parts are concatenated, | ||||
|  * normal one goes first. | ||||
|  * | ||||
|  * Please, keep these structures in sync with emac_stats_keys. | ||||
|  */ | ||||
| 
 | ||||
| /* Normal TX/RX Statistics */ | ||||
| struct emac_stats { | ||||
| 	u64 rx_packets; | ||||
| 	u64 rx_bytes; | ||||
| 	u64 tx_packets; | ||||
| 	u64 tx_bytes; | ||||
| 	u64 rx_packets_csum; | ||||
| 	u64 tx_packets_csum; | ||||
| }; | ||||
| 
 | ||||
| /* Error statistics */ | ||||
| struct emac_error_stats { | ||||
| 	u64 tx_undo; | ||||
| 
 | ||||
| 	/* Software RX Errors */ | ||||
| 	u64 rx_dropped_stack; | ||||
| 	u64 rx_dropped_oom; | ||||
| 	u64 rx_dropped_error; | ||||
| 	u64 rx_dropped_resize; | ||||
| 	u64 rx_dropped_mtu; | ||||
| 	u64 rx_stopped; | ||||
| 	/* BD reported RX errors */ | ||||
| 	u64 rx_bd_errors; | ||||
| 	u64 rx_bd_overrun; | ||||
| 	u64 rx_bd_bad_packet; | ||||
| 	u64 rx_bd_runt_packet; | ||||
| 	u64 rx_bd_short_event; | ||||
| 	u64 rx_bd_alignment_error; | ||||
| 	u64 rx_bd_bad_fcs; | ||||
| 	u64 rx_bd_packet_too_long; | ||||
| 	u64 rx_bd_out_of_range; | ||||
| 	u64 rx_bd_in_range; | ||||
| 	/* EMAC IRQ reported RX errors */ | ||||
| 	u64 rx_parity; | ||||
| 	u64 rx_fifo_overrun; | ||||
| 	u64 rx_overrun; | ||||
| 	u64 rx_bad_packet; | ||||
| 	u64 rx_runt_packet; | ||||
| 	u64 rx_short_event; | ||||
| 	u64 rx_alignment_error; | ||||
| 	u64 rx_bad_fcs; | ||||
| 	u64 rx_packet_too_long; | ||||
| 	u64 rx_out_of_range; | ||||
| 	u64 rx_in_range; | ||||
| 
 | ||||
| 	/* Software TX Errors */ | ||||
| 	u64 tx_dropped; | ||||
| 	/* BD reported TX errors */ | ||||
| 	u64 tx_bd_errors; | ||||
| 	u64 tx_bd_bad_fcs; | ||||
| 	u64 tx_bd_carrier_loss; | ||||
| 	u64 tx_bd_excessive_deferral; | ||||
| 	u64 tx_bd_excessive_collisions; | ||||
| 	u64 tx_bd_late_collision; | ||||
| 	u64 tx_bd_multple_collisions; | ||||
| 	u64 tx_bd_single_collision; | ||||
| 	u64 tx_bd_underrun; | ||||
| 	u64 tx_bd_sqe; | ||||
| 	/* EMAC IRQ reported TX errors */ | ||||
| 	u64 tx_parity; | ||||
| 	u64 tx_underrun; | ||||
| 	u64 tx_sqe; | ||||
| 	u64 tx_errors; | ||||
| }; | ||||
| 
 | ||||
| #define EMAC_ETHTOOL_STATS_COUNT	((sizeof(struct emac_stats) + \ | ||||
| 					  sizeof(struct emac_error_stats)) \ | ||||
| 					 / sizeof(u64)) | ||||
| 
 | ||||
| struct emac_instance { | ||||
| 	struct net_device		*ndev; | ||||
| 	struct resource			rsrc_regs; | ||||
| 	struct emac_regs		__iomem *emacp; | ||||
| 	struct platform_device		*ofdev; | ||||
| 	struct device_node		**blist; /* bootlist entry */ | ||||
| 
 | ||||
| 	/* MAL linkage */ | ||||
| 	u32				mal_ph; | ||||
| 	struct platform_device		*mal_dev; | ||||
| 	u32				mal_rx_chan; | ||||
| 	u32				mal_tx_chan; | ||||
| 	struct mal_instance		*mal; | ||||
| 	struct mal_commac		commac; | ||||
| 
 | ||||
| 	/* PHY infos */ | ||||
| 	u32				phy_mode; | ||||
| 	u32				phy_map; | ||||
| 	u32				phy_address; | ||||
| 	u32				phy_feat_exc; | ||||
| 	struct mii_phy			phy; | ||||
| 	struct mutex			link_lock; | ||||
| 	struct delayed_work		link_work; | ||||
| 	int				link_polling; | ||||
| 
 | ||||
| 	/* GPCS PHY infos */ | ||||
| 	u32				gpcs_address; | ||||
| 
 | ||||
| 	/* Shared MDIO if any */ | ||||
| 	u32				mdio_ph; | ||||
| 	struct platform_device		*mdio_dev; | ||||
| 	struct emac_instance		*mdio_instance; | ||||
| 	struct mutex			mdio_lock; | ||||
| 
 | ||||
| 	/* ZMII infos if any */ | ||||
| 	u32				zmii_ph; | ||||
| 	u32				zmii_port; | ||||
| 	struct platform_device		*zmii_dev; | ||||
| 
 | ||||
| 	/* RGMII infos if any */ | ||||
| 	u32				rgmii_ph; | ||||
| 	u32				rgmii_port; | ||||
| 	struct platform_device		*rgmii_dev; | ||||
| 
 | ||||
| 	/* TAH infos if any */ | ||||
| 	u32				tah_ph; | ||||
| 	u32				tah_port; | ||||
| 	struct platform_device		*tah_dev; | ||||
| 
 | ||||
| 	/* IRQs */ | ||||
| 	int				wol_irq; | ||||
| 	int				emac_irq; | ||||
| 
 | ||||
| 	/* OPB bus frequency in Mhz */ | ||||
| 	u32				opb_bus_freq; | ||||
| 
 | ||||
| 	/* Cell index within an ASIC (for clk mgmnt) */ | ||||
| 	u32				cell_index; | ||||
| 
 | ||||
| 	/* Max supported MTU */ | ||||
| 	u32				max_mtu; | ||||
| 
 | ||||
| 	/* Feature bits (from probe table) */ | ||||
| 	unsigned int			features; | ||||
| 
 | ||||
| 	/* Tx and Rx fifo sizes & other infos in bytes */ | ||||
| 	u32				tx_fifo_size; | ||||
| 	u32				tx_fifo_size_gige; | ||||
| 	u32				rx_fifo_size; | ||||
| 	u32				rx_fifo_size_gige; | ||||
| 	u32				fifo_entry_size; | ||||
| 	u32				mal_burst_size; /* move to MAL ? */ | ||||
| 
 | ||||
| 	/* IAHT and GAHT filter parameterization */ | ||||
| 	u32				xaht_slots_shift; | ||||
| 	u32				xaht_width_shift; | ||||
| 
 | ||||
| 	/* Descriptor management
 | ||||
| 	 */ | ||||
| 	struct mal_descriptor		*tx_desc; | ||||
| 	int				tx_cnt; | ||||
| 	int				tx_slot; | ||||
| 	int				ack_slot; | ||||
| 
 | ||||
| 	struct mal_descriptor		*rx_desc; | ||||
| 	int				rx_slot; | ||||
| 	struct sk_buff			*rx_sg_skb;	/* 1 */ | ||||
| 	int 				rx_skb_size; | ||||
| 	int				rx_sync_size; | ||||
| 
 | ||||
| 	struct sk_buff			*tx_skb[NUM_TX_BUFF]; | ||||
| 	struct sk_buff			*rx_skb[NUM_RX_BUFF]; | ||||
| 
 | ||||
| 	/* Stats
 | ||||
| 	 */ | ||||
| 	struct emac_error_stats		estats; | ||||
| 	struct net_device_stats		nstats; | ||||
| 	struct emac_stats 		stats; | ||||
| 
 | ||||
| 	/* Misc
 | ||||
| 	 */ | ||||
| 	int				reset_failed; | ||||
| 	int				stop_timeout;	/* in us */ | ||||
| 	int				no_mcast; | ||||
| 	int				mcast_pending; | ||||
| 	int				opened; | ||||
| 	struct work_struct		reset_work; | ||||
| 	spinlock_t			lock; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Features of various EMAC implementations | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * No flow control on 40x according to the original driver | ||||
|  */ | ||||
| #define EMAC_FTR_NO_FLOW_CONTROL_40x	0x00000001 | ||||
| /*
 | ||||
|  * Cell is an EMAC4 | ||||
|  */ | ||||
| #define EMAC_FTR_EMAC4			0x00000002 | ||||
| /*
 | ||||
|  * For the 440SPe, AMCC inexplicably changed the polarity of | ||||
|  * the "operation complete" bit in the MII control register. | ||||
|  */ | ||||
| #define EMAC_FTR_STACR_OC_INVERT	0x00000004 | ||||
| /*
 | ||||
|  * Set if we have a TAH. | ||||
|  */ | ||||
| #define EMAC_FTR_HAS_TAH		0x00000008 | ||||
| /*
 | ||||
|  * Set if we have a ZMII. | ||||
|  */ | ||||
| #define EMAC_FTR_HAS_ZMII		0x00000010 | ||||
| /*
 | ||||
|  * Set if we have a RGMII. | ||||
|  */ | ||||
| #define EMAC_FTR_HAS_RGMII		0x00000020 | ||||
| /*
 | ||||
|  * Set if we have new type STACR with STAOPC | ||||
|  */ | ||||
| #define EMAC_FTR_HAS_NEW_STACR		0x00000040 | ||||
| /*
 | ||||
|  * Set if we need phy clock workaround for 440gx | ||||
|  */ | ||||
| #define EMAC_FTR_440GX_PHY_CLK_FIX	0x00000080 | ||||
| /*
 | ||||
|  * Set if we need phy clock workaround for 440ep or 440gr | ||||
|  */ | ||||
| #define EMAC_FTR_440EP_PHY_CLK_FIX	0x00000100 | ||||
| /*
 | ||||
|  * The 405EX and 460EX contain the EMAC4SYNC core | ||||
|  */ | ||||
| #define EMAC_FTR_EMAC4SYNC		0x00000200 | ||||
| /*
 | ||||
|  * Set if we need phy clock workaround for 460ex or 460gt | ||||
|  */ | ||||
| #define EMAC_FTR_460EX_PHY_CLK_FIX	0x00000400 | ||||
| /*
 | ||||
|  * APM821xx requires Jumbo frame size set explicitly | ||||
|  */ | ||||
| #define EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE	0x00000800 | ||||
| /*
 | ||||
|  * APM821xx does not support Half Duplex mode | ||||
|  */ | ||||
| #define EMAC_FTR_APM821XX_NO_HALF_DUPLEX	0x00001000 | ||||
| 
 | ||||
| /* Right now, we don't quite handle the always/possible masks on the
 | ||||
|  * most optimal way as we don't have a way to say something like | ||||
|  * always EMAC4. Patches welcome. | ||||
|  */ | ||||
| enum { | ||||
| 	EMAC_FTRS_ALWAYS	= 0, | ||||
| 
 | ||||
| 	EMAC_FTRS_POSSIBLE	= | ||||
| #ifdef CONFIG_IBM_EMAC_EMAC4 | ||||
| 	    EMAC_FTR_EMAC4	| EMAC_FTR_EMAC4SYNC	| | ||||
| 	    EMAC_FTR_HAS_NEW_STACR	| | ||||
| 	    EMAC_FTR_STACR_OC_INVERT | EMAC_FTR_440GX_PHY_CLK_FIX | | ||||
| #endif | ||||
| #ifdef CONFIG_IBM_EMAC_TAH | ||||
| 	    EMAC_FTR_HAS_TAH	| | ||||
| #endif | ||||
| #ifdef CONFIG_IBM_EMAC_ZMII | ||||
| 	    EMAC_FTR_HAS_ZMII	| | ||||
| #endif | ||||
| #ifdef CONFIG_IBM_EMAC_RGMII | ||||
| 	    EMAC_FTR_HAS_RGMII	| | ||||
| #endif | ||||
| #ifdef CONFIG_IBM_EMAC_NO_FLOW_CTRL | ||||
| 	    EMAC_FTR_NO_FLOW_CONTROL_40x | | ||||
| #endif | ||||
| 	EMAC_FTR_460EX_PHY_CLK_FIX | | ||||
| 	EMAC_FTR_440EP_PHY_CLK_FIX | | ||||
| 	EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE | | ||||
| 	EMAC_FTR_APM821XX_NO_HALF_DUPLEX, | ||||
| }; | ||||
| 
 | ||||
| static inline int emac_has_feature(struct emac_instance *dev, | ||||
| 				   unsigned long feature) | ||||
| { | ||||
| 	return (EMAC_FTRS_ALWAYS & feature) || | ||||
| 	       (EMAC_FTRS_POSSIBLE & dev->features & feature); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Various instances of the EMAC core have varying 1) number of | ||||
|  * address match slots, 2) width of the registers for handling address | ||||
|  * match slots, 3) number of registers for handling address match | ||||
|  * slots and 4) base offset for those registers. | ||||
|  * | ||||
|  * These macros and inlines handle these differences based on | ||||
|  * parameters supplied by the device structure which are, in turn, | ||||
|  * initialized based on the "compatible" entry in the device tree. | ||||
|  */ | ||||
| 
 | ||||
| #define	EMAC4_XAHT_SLOTS_SHIFT		6 | ||||
| #define	EMAC4_XAHT_WIDTH_SHIFT		4 | ||||
| 
 | ||||
| #define	EMAC4SYNC_XAHT_SLOTS_SHIFT	8 | ||||
| #define	EMAC4SYNC_XAHT_WIDTH_SHIFT	5 | ||||
| 
 | ||||
| #define	EMAC_XAHT_SLOTS(dev)         	(1 << (dev)->xaht_slots_shift) | ||||
| #define	EMAC_XAHT_WIDTH(dev)         	(1 << (dev)->xaht_width_shift) | ||||
| #define	EMAC_XAHT_REGS(dev)          	(1 << ((dev)->xaht_slots_shift - \ | ||||
| 					       (dev)->xaht_width_shift)) | ||||
| 
 | ||||
| #define	EMAC_XAHT_CRC_TO_SLOT(dev, crc)			\ | ||||
| 	((EMAC_XAHT_SLOTS(dev) - 1) -			\ | ||||
| 	 ((crc) >> ((sizeof (u32) * BITS_PER_BYTE) -	\ | ||||
| 		    (dev)->xaht_slots_shift))) | ||||
| 
 | ||||
| #define	EMAC_XAHT_SLOT_TO_REG(dev, slot)		\ | ||||
| 	((slot) >> (dev)->xaht_width_shift) | ||||
| 
 | ||||
| #define	EMAC_XAHT_SLOT_TO_MASK(dev, slot)		\ | ||||
| 	((u32)(1 << (EMAC_XAHT_WIDTH(dev) - 1)) >>	\ | ||||
| 	 ((slot) & (u32)(EMAC_XAHT_WIDTH(dev) - 1))) | ||||
| 
 | ||||
| static inline u32 *emac_xaht_base(struct emac_instance *dev) | ||||
| { | ||||
| 	struct emac_regs __iomem *p = dev->emacp; | ||||
| 	int offset; | ||||
| 
 | ||||
| 	/* The first IAHT entry always is the base of the block of
 | ||||
| 	 * IAHT and GAHT registers. | ||||
| 	 */ | ||||
| 	if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC)) | ||||
| 		offset = offsetof(struct emac_regs, u1.emac4sync.iaht1); | ||||
| 	else | ||||
| 		offset = offsetof(struct emac_regs, u0.emac4.iaht1); | ||||
| 
 | ||||
| 	return (u32 *)((ptrdiff_t)p + offset); | ||||
| } | ||||
| 
 | ||||
| static inline u32 *emac_gaht_base(struct emac_instance *dev) | ||||
| { | ||||
| 	/* GAHT registers always come after an identical number of
 | ||||
| 	 * IAHT registers. | ||||
| 	 */ | ||||
| 	return emac_xaht_base(dev) + EMAC_XAHT_REGS(dev); | ||||
| } | ||||
| 
 | ||||
| static inline u32 *emac_iaht_base(struct emac_instance *dev) | ||||
| { | ||||
| 	/* IAHT registers always come before an identical number of
 | ||||
| 	 * GAHT registers. | ||||
| 	 */ | ||||
| 	return emac_xaht_base(dev); | ||||
| } | ||||
| 
 | ||||
| /* Ethtool get_regs complex data.
 | ||||
|  * We want to get not just EMAC registers, but also MAL, ZMII, RGMII, TAH | ||||
|  * when available. | ||||
|  * | ||||
|  * Returned BLOB consists of the ibm_emac_ethtool_regs_hdr, | ||||
|  * MAL registers, EMAC registers and optional ZMII, RGMII, TAH registers. | ||||
|  * Each register component is preceded with emac_ethtool_regs_subhdr. | ||||
|  * Order of the optional headers follows their relative bit posititions | ||||
|  * in emac_ethtool_regs_hdr.components | ||||
|  */ | ||||
| #define EMAC_ETHTOOL_REGS_ZMII		0x00000001 | ||||
| #define EMAC_ETHTOOL_REGS_RGMII		0x00000002 | ||||
| #define EMAC_ETHTOOL_REGS_TAH		0x00000004 | ||||
| 
 | ||||
| struct emac_ethtool_regs_hdr { | ||||
| 	u32 components; | ||||
| }; | ||||
| 
 | ||||
| struct emac_ethtool_regs_subhdr { | ||||
| 	u32 version; | ||||
| 	u32 index; | ||||
| }; | ||||
| 
 | ||||
| #define EMAC_ETHTOOL_REGS_VER		0 | ||||
| #define EMAC_ETHTOOL_REGS_SIZE(dev) 	((dev)->rsrc_regs.end - \ | ||||
| 					 (dev)->rsrc_regs.start + 1) | ||||
| #define EMAC4_ETHTOOL_REGS_VER      	1 | ||||
| #define EMAC4_ETHTOOL_REGS_SIZE(dev)	((dev)->rsrc_regs.end -	\ | ||||
| 					 (dev)->rsrc_regs.start + 1) | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_CORE_H */ | ||||
							
								
								
									
										270
									
								
								drivers/net/ethernet/ibm/emac/debug.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								drivers/net/ethernet/ibm/emac/debug.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,270 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/debug.c | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * 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/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/sysrq.h> | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| #include "core.h" | ||||
| 
 | ||||
| static DEFINE_SPINLOCK(emac_dbg_lock); | ||||
| 
 | ||||
| static void emac_desc_dump(struct emac_instance *p) | ||||
| { | ||||
| 	int i; | ||||
| 	printk("** EMAC %s TX BDs **\n" | ||||
| 	       " tx_cnt = %d tx_slot = %d ack_slot = %d\n", | ||||
| 	       p->ofdev->dev.of_node->full_name, | ||||
| 	       p->tx_cnt, p->tx_slot, p->ack_slot); | ||||
| 	for (i = 0; i < NUM_TX_BUFF / 2; ++i) | ||||
| 		printk | ||||
| 		    ("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n", | ||||
| 		     i, p->tx_desc[i].data_ptr, p->tx_skb[i] ? 'V' : ' ', | ||||
| 		     p->tx_desc[i].ctrl, p->tx_desc[i].data_len, | ||||
| 		     NUM_TX_BUFF / 2 + i, | ||||
| 		     p->tx_desc[NUM_TX_BUFF / 2 + i].data_ptr, | ||||
| 		     p->tx_skb[NUM_TX_BUFF / 2 + i] ? 'V' : ' ', | ||||
| 		     p->tx_desc[NUM_TX_BUFF / 2 + i].ctrl, | ||||
| 		     p->tx_desc[NUM_TX_BUFF / 2 + i].data_len); | ||||
| 
 | ||||
| 	printk("** EMAC %s RX BDs **\n" | ||||
| 	       " rx_slot = %d flags = 0x%lx rx_skb_size = %d rx_sync_size = %d\n" | ||||
| 	       " rx_sg_skb = 0x%p\n", | ||||
| 	       p->ofdev->dev.of_node->full_name, | ||||
| 	       p->rx_slot, p->commac.flags, p->rx_skb_size, | ||||
| 	       p->rx_sync_size, p->rx_sg_skb); | ||||
| 	for (i = 0; i < NUM_RX_BUFF / 2; ++i) | ||||
| 		printk | ||||
| 		    ("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n", | ||||
| 		     i, p->rx_desc[i].data_ptr, p->rx_skb[i] ? 'V' : ' ', | ||||
| 		     p->rx_desc[i].ctrl, p->rx_desc[i].data_len, | ||||
| 		     NUM_RX_BUFF / 2 + i, | ||||
| 		     p->rx_desc[NUM_RX_BUFF / 2 + i].data_ptr, | ||||
| 		     p->rx_skb[NUM_RX_BUFF / 2 + i] ? 'V' : ' ', | ||||
| 		     p->rx_desc[NUM_RX_BUFF / 2 + i].ctrl, | ||||
| 		     p->rx_desc[NUM_RX_BUFF / 2 + i].data_len); | ||||
| } | ||||
| 
 | ||||
| static void emac_mac_dump(struct emac_instance *dev) | ||||
| { | ||||
| 	struct emac_regs __iomem *p = dev->emacp; | ||||
| 	const int xaht_regs = EMAC_XAHT_REGS(dev); | ||||
| 	u32 *gaht_base = emac_gaht_base(dev); | ||||
| 	u32 *iaht_base = emac_iaht_base(dev); | ||||
| 	int emac4sync = emac_has_feature(dev, EMAC_FTR_EMAC4SYNC); | ||||
| 	int n; | ||||
| 
 | ||||
| 	printk("** EMAC %s registers **\n" | ||||
| 	       "MR0 = 0x%08x MR1 = 0x%08x TMR0 = 0x%08x TMR1 = 0x%08x\n" | ||||
| 	       "RMR = 0x%08x ISR = 0x%08x ISER = 0x%08x\n" | ||||
| 	       "IAR = %04x%08x VTPID = 0x%04x VTCI = 0x%04x\n", | ||||
| 	       dev->ofdev->dev.of_node->full_name, | ||||
| 	       in_be32(&p->mr0), in_be32(&p->mr1), | ||||
| 	       in_be32(&p->tmr0), in_be32(&p->tmr1), | ||||
| 	       in_be32(&p->rmr), in_be32(&p->isr), in_be32(&p->iser), | ||||
| 	       in_be32(&p->iahr), in_be32(&p->ialr), in_be32(&p->vtpid), | ||||
| 	       in_be32(&p->vtci) | ||||
| 	       ); | ||||
| 
 | ||||
| 	if (emac4sync) | ||||
| 		printk("MAR = %04x%08x MMAR = %04x%08x\n", | ||||
| 		       in_be32(&p->u0.emac4sync.mahr), | ||||
| 		       in_be32(&p->u0.emac4sync.malr), | ||||
| 		       in_be32(&p->u0.emac4sync.mmahr), | ||||
| 		       in_be32(&p->u0.emac4sync.mmalr) | ||||
| 		       ); | ||||
| 
 | ||||
| 	for (n = 0; n < xaht_regs; n++) | ||||
| 		printk("IAHT%02d = 0x%08x\n", n + 1, in_be32(iaht_base + n)); | ||||
| 
 | ||||
| 	for (n = 0; n < xaht_regs; n++) | ||||
| 		printk("GAHT%02d = 0x%08x\n", n + 1, in_be32(gaht_base + n)); | ||||
| 
 | ||||
| 	printk("LSA = %04x%08x IPGVR = 0x%04x\n" | ||||
| 	       "STACR = 0x%08x TRTR = 0x%08x RWMR = 0x%08x\n" | ||||
| 	       "OCTX = 0x%08x OCRX = 0x%08x\n", | ||||
| 	       in_be32(&p->lsah), in_be32(&p->lsal), in_be32(&p->ipgvr), | ||||
| 	       in_be32(&p->stacr), in_be32(&p->trtr), in_be32(&p->rwmr), | ||||
| 	       in_be32(&p->octx), in_be32(&p->ocrx) | ||||
| 	       ); | ||||
| 
 | ||||
| 	if (!emac4sync) { | ||||
| 		printk("IPCR = 0x%08x\n", | ||||
| 		       in_be32(&p->u1.emac4.ipcr) | ||||
| 		       ); | ||||
| 	} else { | ||||
| 		printk("REVID = 0x%08x TPC = 0x%08x\n", | ||||
| 		       in_be32(&p->u1.emac4sync.revid), | ||||
| 		       in_be32(&p->u1.emac4sync.tpc) | ||||
| 		       ); | ||||
| 	} | ||||
| 
 | ||||
| 	emac_desc_dump(dev); | ||||
| } | ||||
| 
 | ||||
| static void emac_mal_dump(struct mal_instance *mal) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	printk("** MAL %s Registers **\n" | ||||
| 	       "CFG = 0x%08x ESR = 0x%08x IER = 0x%08x\n" | ||||
| 	       "TX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n" | ||||
| 	       "RX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n", | ||||
| 	       mal->ofdev->dev.of_node->full_name, | ||||
| 	       get_mal_dcrn(mal, MAL_CFG), get_mal_dcrn(mal, MAL_ESR), | ||||
| 	       get_mal_dcrn(mal, MAL_IER), | ||||
| 	       get_mal_dcrn(mal, MAL_TXCASR), get_mal_dcrn(mal, MAL_TXCARR), | ||||
| 	       get_mal_dcrn(mal, MAL_TXEOBISR), get_mal_dcrn(mal, MAL_TXDEIR), | ||||
| 	       get_mal_dcrn(mal, MAL_RXCASR), get_mal_dcrn(mal, MAL_RXCARR), | ||||
| 	       get_mal_dcrn(mal, MAL_RXEOBISR), get_mal_dcrn(mal, MAL_RXDEIR) | ||||
| 	    ); | ||||
| 
 | ||||
| 	printk("TX|"); | ||||
| 	for (i = 0; i < mal->num_tx_chans; ++i) { | ||||
| 		if (i && !(i % 4)) | ||||
| 			printk("\n   "); | ||||
| 		printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_TXCTPR(i))); | ||||
| 	} | ||||
| 	printk("\nRX|"); | ||||
| 	for (i = 0; i < mal->num_rx_chans; ++i) { | ||||
| 		if (i && !(i % 4)) | ||||
| 			printk("\n   "); | ||||
| 		printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_RXCTPR(i))); | ||||
| 	} | ||||
| 	printk("\n   "); | ||||
| 	for (i = 0; i < mal->num_rx_chans; ++i) { | ||||
| 		u32 r = get_mal_dcrn(mal, MAL_RCBS(i)); | ||||
| 		if (i && !(i % 3)) | ||||
| 			printk("\n   "); | ||||
| 		printk("RCBS%d = 0x%08x (%d) ", i, r, r * 16); | ||||
| 	} | ||||
| 	printk("\n"); | ||||
| } | ||||
| 
 | ||||
| static struct emac_instance *__emacs[4]; | ||||
| static struct mal_instance *__mals[1]; | ||||
| 
 | ||||
| void emac_dbg_register(struct emac_instance *dev) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&emac_dbg_lock, flags); | ||||
| 	for (i = 0; i < ARRAY_SIZE(__emacs); i++) | ||||
| 		if (__emacs[i] == NULL) { | ||||
| 			__emacs[i] = dev; | ||||
| 			break; | ||||
| 		} | ||||
| 	spin_unlock_irqrestore(&emac_dbg_lock, flags); | ||||
| } | ||||
| 
 | ||||
| void emac_dbg_unregister(struct emac_instance *dev) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&emac_dbg_lock, flags); | ||||
| 	for (i = 0; i < ARRAY_SIZE(__emacs); i++) | ||||
| 		if (__emacs[i] == dev) { | ||||
| 			__emacs[i] = NULL; | ||||
| 			break; | ||||
| 		} | ||||
| 	spin_unlock_irqrestore(&emac_dbg_lock, flags); | ||||
| } | ||||
| 
 | ||||
| void mal_dbg_register(struct mal_instance *mal) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&emac_dbg_lock, flags); | ||||
| 	for (i = 0; i < ARRAY_SIZE(__mals); i++) | ||||
| 		if (__mals[i] == NULL) { | ||||
| 			__mals[i] = mal; | ||||
| 			break; | ||||
| 		} | ||||
| 	spin_unlock_irqrestore(&emac_dbg_lock, flags); | ||||
| } | ||||
| 
 | ||||
| void mal_dbg_unregister(struct mal_instance *mal) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	int i; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&emac_dbg_lock, flags); | ||||
| 	for (i = 0; i < ARRAY_SIZE(__mals); i++) | ||||
| 		if (__mals[i] == mal) { | ||||
| 			__mals[i] = NULL; | ||||
| 			break; | ||||
| 		} | ||||
| 	spin_unlock_irqrestore(&emac_dbg_lock, flags); | ||||
| } | ||||
| 
 | ||||
| void emac_dbg_dump_all(void) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&emac_dbg_lock, flags); | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(__mals); ++i) | ||||
| 		if (__mals[i]) | ||||
| 			emac_mal_dump(__mals[i]); | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(__emacs); ++i) | ||||
| 		if (__emacs[i]) | ||||
| 			emac_mac_dump(__emacs[i]); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&emac_dbg_lock, flags); | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_MAGIC_SYSRQ) | ||||
| static void emac_sysrq_handler(int key) | ||||
| { | ||||
| 	emac_dbg_dump_all(); | ||||
| } | ||||
| 
 | ||||
| static struct sysrq_key_op emac_sysrq_op = { | ||||
| 	.handler = emac_sysrq_handler, | ||||
| 	.help_msg = "emac(c)", | ||||
| 	.action_msg = "Show EMAC(s) status", | ||||
| }; | ||||
| 
 | ||||
| int __init emac_init_debug(void) | ||||
| { | ||||
| 	return register_sysrq_key('c', &emac_sysrq_op); | ||||
| } | ||||
| 
 | ||||
| void __exit emac_fini_debug(void) | ||||
| { | ||||
| 	unregister_sysrq_key('c', &emac_sysrq_op); | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| int __init emac_init_debug(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| void __exit emac_fini_debug(void) | ||||
| { | ||||
| } | ||||
| #endif				/* CONFIG_MAGIC_SYSRQ */ | ||||
							
								
								
									
										83
									
								
								drivers/net/ethernet/ibm/emac/debug.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								drivers/net/ethernet/ibm/emac/debug.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/debug.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * 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. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __IBM_NEWEMAC_DEBUG_H | ||||
| #define __IBM_NEWEMAC_DEBUG_H | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| 
 | ||||
| #include "core.h" | ||||
| 
 | ||||
| #if defined(CONFIG_IBM_EMAC_DEBUG) | ||||
| 
 | ||||
| struct emac_instance; | ||||
| struct mal_instance; | ||||
| 
 | ||||
| void emac_dbg_register(struct emac_instance *dev); | ||||
| void emac_dbg_unregister(struct emac_instance *dev); | ||||
| void mal_dbg_register(struct mal_instance *mal); | ||||
| void mal_dbg_unregister(struct mal_instance *mal); | ||||
| int emac_init_debug(void) __init; | ||||
| void emac_fini_debug(void) __exit; | ||||
| void emac_dbg_dump_all(void); | ||||
| 
 | ||||
| # define DBG_LEVEL		1 | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| # define emac_dbg_register(x)	do { } while(0) | ||||
| # define emac_dbg_unregister(x)	do { } while(0) | ||||
| # define mal_dbg_register(x)	do { } while(0) | ||||
| # define mal_dbg_unregister(x)	do { } while(0) | ||||
| # define emac_init_debug()	do { } while(0) | ||||
| # define emac_fini_debug()	do { } while(0) | ||||
| # define emac_dbg_dump_all()	do { } while(0) | ||||
| 
 | ||||
| # define DBG_LEVEL		0 | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #define EMAC_DBG(d, name, fmt, arg...) \ | ||||
| 	printk(KERN_DEBUG #name "%s: " fmt, d->ofdev->dev.of_node->full_name, ## arg) | ||||
| 
 | ||||
| #if DBG_LEVEL > 0 | ||||
| #  define DBG(d,f,x...)		EMAC_DBG(d, emac, f, ##x) | ||||
| #  define MAL_DBG(d,f,x...)	EMAC_DBG(d, mal, f, ##x) | ||||
| #  define ZMII_DBG(d,f,x...)	EMAC_DBG(d, zmii, f, ##x) | ||||
| #  define RGMII_DBG(d,f,x...)	EMAC_DBG(d, rgmii, f, ##x) | ||||
| #  define NL			"\n" | ||||
| #else | ||||
| #  define DBG(f,x...)		((void)0) | ||||
| #  define MAL_DBG(d,f,x...)	((void)0) | ||||
| #  define ZMII_DBG(d,f,x...)	((void)0) | ||||
| #  define RGMII_DBG(d,f,x...)	((void)0) | ||||
| #endif | ||||
| #if DBG_LEVEL > 1 | ||||
| #  define DBG2(d,f,x...) 	DBG(d,f, ##x) | ||||
| #  define MAL_DBG2(d,f,x...) 	MAL_DBG(d,f, ##x) | ||||
| #  define ZMII_DBG2(d,f,x...) 	ZMII_DBG(d,f, ##x) | ||||
| #  define RGMII_DBG2(d,f,x...) 	RGMII_DBG(d,f, ##x) | ||||
| #else | ||||
| #  define DBG2(f,x...) 		((void)0) | ||||
| #  define MAL_DBG2(d,f,x...) 	((void)0) | ||||
| #  define ZMII_DBG2(d,f,x...) 	((void)0) | ||||
| #  define RGMII_DBG2(d,f,x...) 	((void)0) | ||||
| #endif | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_DEBUG_H */ | ||||
							
								
								
									
										314
									
								
								drivers/net/ethernet/ibm/emac/emac.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								drivers/net/ethernet/ibm/emac/emac.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,314 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/emac.h | ||||
|  * | ||||
|  * Register definitions for PowerPC 4xx on-chip ethernet contoller | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Matt Porter <mporter@kernel.crashing.org> | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  * 	Copyright 2002-2004 MontaVista Software 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. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __IBM_NEWEMAC_H | ||||
| #define __IBM_NEWEMAC_H | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/phy.h> | ||||
| 
 | ||||
| /* EMAC registers 			Write Access rules */ | ||||
| struct emac_regs { | ||||
| 	/* Common registers across all EMAC implementations. */ | ||||
| 	u32 mr0;			/* Special 	*/ | ||||
| 	u32 mr1;			/* Reset 	*/ | ||||
| 	u32 tmr0;			/* Special 	*/ | ||||
| 	u32 tmr1;			/* Special 	*/ | ||||
| 	u32 rmr;			/* Reset 	*/ | ||||
| 	u32 isr;			/* Always 	*/ | ||||
| 	u32 iser;			/* Reset 	*/ | ||||
| 	u32 iahr;			/* Reset, R, T 	*/ | ||||
| 	u32 ialr;			/* Reset, R, T 	*/ | ||||
| 	u32 vtpid;			/* Reset, R, T 	*/ | ||||
| 	u32 vtci;			/* Reset, R, T 	*/ | ||||
| 	u32 ptr;			/* Reset,    T 	*/ | ||||
| 	union { | ||||
| 		/* Registers unique to EMAC4 implementations */ | ||||
| 		struct { | ||||
| 			u32 iaht1;	/* Reset, R	*/ | ||||
| 			u32 iaht2;	/* Reset, R	*/ | ||||
| 			u32 iaht3;	/* Reset, R	*/ | ||||
| 			u32 iaht4;	/* Reset, R	*/ | ||||
| 			u32 gaht1;	/* Reset, R	*/ | ||||
| 			u32 gaht2;	/* Reset, R	*/ | ||||
| 			u32 gaht3;	/* Reset, R	*/ | ||||
| 			u32 gaht4;	/* Reset, R	*/ | ||||
| 		} emac4; | ||||
| 		/* Registers unique to EMAC4SYNC implementations */ | ||||
| 		struct { | ||||
| 			u32 mahr;	/* Reset, R, T  */ | ||||
| 			u32 malr;	/* Reset, R, T  */ | ||||
| 			u32 mmahr;	/* Reset, R, T  */ | ||||
| 			u32 mmalr;	/* Reset, R, T  */ | ||||
| 			u32 rsvd0[4]; | ||||
| 		} emac4sync; | ||||
| 	} u0; | ||||
| 	/* Common registers across all EMAC implementations. */ | ||||
| 	u32 lsah; | ||||
| 	u32 lsal; | ||||
| 	u32 ipgvr;			/* Reset,    T 	*/ | ||||
| 	u32 stacr;			/* Special 	*/ | ||||
| 	u32 trtr;			/* Special 	*/ | ||||
| 	u32 rwmr;			/* Reset 	*/ | ||||
| 	u32 octx; | ||||
| 	u32 ocrx; | ||||
| 	union { | ||||
| 		/* Registers unique to EMAC4 implementations */ | ||||
| 		struct { | ||||
| 			u32 ipcr; | ||||
| 		} emac4; | ||||
| 		/* Registers unique to EMAC4SYNC implementations */ | ||||
| 		struct { | ||||
| 			u32 rsvd1; | ||||
| 			u32 revid; | ||||
|  			u32 rsvd2[2]; | ||||
| 			u32 iaht1;	/* Reset, R     */ | ||||
| 			u32 iaht2;	/* Reset, R     */ | ||||
| 			u32 iaht3;	/* Reset, R     */ | ||||
| 			u32 iaht4;	/* Reset, R     */ | ||||
| 			u32 iaht5;	/* Reset, R     */ | ||||
| 			u32 iaht6;	/* Reset, R     */ | ||||
| 			u32 iaht7;	/* Reset, R     */ | ||||
| 			u32 iaht8;	/* Reset, R     */ | ||||
| 			u32 gaht1;	/* Reset, R     */ | ||||
| 			u32 gaht2;	/* Reset, R     */ | ||||
| 			u32 gaht3;	/* Reset, R     */ | ||||
| 			u32 gaht4;	/* Reset, R     */ | ||||
| 			u32 gaht5;	/* Reset, R     */ | ||||
| 			u32 gaht6;	/* Reset, R     */ | ||||
| 			u32 gaht7;	/* Reset, R     */ | ||||
| 			u32 gaht8;	/* Reset, R     */ | ||||
| 			u32 tpc;	/* Reset, T     */ | ||||
| 		} emac4sync; | ||||
| 	} u1; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * PHY mode settings (EMAC <-> ZMII/RGMII bridge <-> PHY) | ||||
|  */ | ||||
| #define PHY_MODE_NA	PHY_INTERFACE_MODE_NA | ||||
| #define PHY_MODE_MII	PHY_INTERFACE_MODE_MII | ||||
| #define PHY_MODE_RMII	PHY_INTERFACE_MODE_RMII | ||||
| #define PHY_MODE_SMII	PHY_INTERFACE_MODE_SMII | ||||
| #define PHY_MODE_RGMII	PHY_INTERFACE_MODE_RGMII | ||||
| #define PHY_MODE_TBI	PHY_INTERFACE_MODE_TBI | ||||
| #define PHY_MODE_GMII	PHY_INTERFACE_MODE_GMII | ||||
| #define PHY_MODE_RTBI	PHY_INTERFACE_MODE_RTBI | ||||
| #define PHY_MODE_SGMII	PHY_INTERFACE_MODE_SGMII | ||||
| 
 | ||||
| /* EMACx_MR0 */ | ||||
| #define EMAC_MR0_RXI			0x80000000 | ||||
| #define EMAC_MR0_TXI			0x40000000 | ||||
| #define EMAC_MR0_SRST			0x20000000 | ||||
| #define EMAC_MR0_TXE			0x10000000 | ||||
| #define EMAC_MR0_RXE			0x08000000 | ||||
| #define EMAC_MR0_WKE			0x04000000 | ||||
| 
 | ||||
| /* EMACx_MR1 */ | ||||
| #define EMAC_MR1_FDE			0x80000000 | ||||
| #define EMAC_MR1_ILE			0x40000000 | ||||
| #define EMAC_MR1_VLE			0x20000000 | ||||
| #define EMAC_MR1_EIFC			0x10000000 | ||||
| #define EMAC_MR1_APP			0x08000000 | ||||
| #define EMAC_MR1_IST			0x01000000 | ||||
| 
 | ||||
| #define EMAC_MR1_MF_MASK		0x00c00000 | ||||
| #define EMAC_MR1_MF_10			0x00000000 | ||||
| #define EMAC_MR1_MF_100			0x00400000 | ||||
| #define EMAC_MR1_MF_1000		0x00800000 | ||||
| #define EMAC_MR1_MF_1000GPCS		0x00c00000 | ||||
| #define EMAC_MR1_MF_IPPA(id)		(((id) & 0x1f) << 6) | ||||
| 
 | ||||
| #define EMAC_MR1_RFS_4K			0x00300000 | ||||
| #define EMAC_MR1_RFS_16K		0x00000000 | ||||
| #define EMAC_MR1_TFS_2K			0x00080000 | ||||
| #define EMAC_MR1_TR0_MULT		0x00008000 | ||||
| #define EMAC_MR1_JPSM			0x00000000 | ||||
| #define EMAC_MR1_MWSW_001		0x00000000 | ||||
| #define EMAC_MR1_BASE(opb)		(EMAC_MR1_TFS_2K | EMAC_MR1_TR0_MULT) | ||||
| 
 | ||||
| 
 | ||||
| #define EMAC4_MR1_RFS_2K		0x00100000 | ||||
| #define EMAC4_MR1_RFS_4K		0x00180000 | ||||
| #define EMAC4_MR1_RFS_16K		0x00280000 | ||||
| #define EMAC4_MR1_TFS_2K       		0x00020000 | ||||
| #define EMAC4_MR1_TFS_4K		0x00030000 | ||||
| #define EMAC4_MR1_TFS_16K		0x00050000 | ||||
| #define EMAC4_MR1_TR			0x00008000 | ||||
| #define EMAC4_MR1_MWSW_001		0x00001000 | ||||
| #define EMAC4_MR1_JPSM			0x00000800 | ||||
| #define EMAC4_MR1_OBCI_MASK		0x00000038 | ||||
| #define EMAC4_MR1_OBCI_50		0x00000000 | ||||
| #define EMAC4_MR1_OBCI_66		0x00000008 | ||||
| #define EMAC4_MR1_OBCI_83		0x00000010 | ||||
| #define EMAC4_MR1_OBCI_100		0x00000018 | ||||
| #define EMAC4_MR1_OBCI_100P		0x00000020 | ||||
| #define EMAC4_MR1_OBCI(freq)		((freq) <= 50  ? EMAC4_MR1_OBCI_50 : \ | ||||
| 					 (freq) <= 66  ? EMAC4_MR1_OBCI_66 : \ | ||||
| 					 (freq) <= 83  ? EMAC4_MR1_OBCI_83 : \ | ||||
| 					 (freq) <= 100 ? EMAC4_MR1_OBCI_100 : \ | ||||
| 						EMAC4_MR1_OBCI_100P) | ||||
| 
 | ||||
| /* EMACx_TMR0 */ | ||||
| #define EMAC_TMR0_GNP			0x80000000 | ||||
| #define EMAC_TMR0_DEFAULT		0x00000000 | ||||
| #define EMAC4_TMR0_TFAE_2_32		0x00000001 | ||||
| #define EMAC4_TMR0_TFAE_4_64		0x00000002 | ||||
| #define EMAC4_TMR0_TFAE_8_128		0x00000003 | ||||
| #define EMAC4_TMR0_TFAE_16_256		0x00000004 | ||||
| #define EMAC4_TMR0_TFAE_32_512		0x00000005 | ||||
| #define EMAC4_TMR0_TFAE_64_1024		0x00000006 | ||||
| #define EMAC4_TMR0_TFAE_128_2048	0x00000007 | ||||
| #define EMAC4_TMR0_DEFAULT		EMAC4_TMR0_TFAE_2_32 | ||||
| #define EMAC_TMR0_XMIT			(EMAC_TMR0_GNP | EMAC_TMR0_DEFAULT) | ||||
| #define EMAC4_TMR0_XMIT			(EMAC_TMR0_GNP | EMAC4_TMR0_DEFAULT) | ||||
| 
 | ||||
| /* EMACx_TMR1 */ | ||||
| 
 | ||||
| #define EMAC_TMR1(l,h)			(((l) << 27) | (((h) & 0xff) << 16)) | ||||
| #define EMAC4_TMR1(l,h)			(((l) << 27) | (((h) & 0x3ff) << 14)) | ||||
| 
 | ||||
| /* EMACx_RMR */ | ||||
| #define EMAC_RMR_SP			0x80000000 | ||||
| #define EMAC_RMR_SFCS			0x40000000 | ||||
| #define EMAC_RMR_RRP			0x20000000 | ||||
| #define EMAC_RMR_RFP			0x10000000 | ||||
| #define EMAC_RMR_ROP			0x08000000 | ||||
| #define EMAC_RMR_RPIR			0x04000000 | ||||
| #define EMAC_RMR_PPP			0x02000000 | ||||
| #define EMAC_RMR_PME			0x01000000 | ||||
| #define EMAC_RMR_PMME			0x00800000 | ||||
| #define EMAC_RMR_IAE			0x00400000 | ||||
| #define EMAC_RMR_MIAE			0x00200000 | ||||
| #define EMAC_RMR_BAE			0x00100000 | ||||
| #define EMAC_RMR_MAE			0x00080000 | ||||
| #define EMAC_RMR_BASE			0x00000000 | ||||
| #define EMAC4_RMR_RFAF_2_32		0x00000001 | ||||
| #define EMAC4_RMR_RFAF_4_64		0x00000002 | ||||
| #define EMAC4_RMR_RFAF_8_128		0x00000003 | ||||
| #define EMAC4_RMR_RFAF_16_256		0x00000004 | ||||
| #define EMAC4_RMR_RFAF_32_512		0x00000005 | ||||
| #define EMAC4_RMR_RFAF_64_1024		0x00000006 | ||||
| #define EMAC4_RMR_RFAF_128_2048		0x00000007 | ||||
| #define EMAC4_RMR_BASE			EMAC4_RMR_RFAF_128_2048 | ||||
| #define EMAC4_RMR_MJS_MASK              0x0001fff8 | ||||
| #define EMAC4_RMR_MJS(s)                (((s) << 3) & EMAC4_RMR_MJS_MASK) | ||||
| 
 | ||||
| /* EMACx_ISR & EMACx_ISER */ | ||||
| #define EMAC4_ISR_TXPE			0x20000000 | ||||
| #define EMAC4_ISR_RXPE			0x10000000 | ||||
| #define EMAC4_ISR_TXUE			0x08000000 | ||||
| #define EMAC4_ISR_RXOE			0x04000000 | ||||
| #define EMAC_ISR_OVR			0x02000000 | ||||
| #define EMAC_ISR_PP			0x01000000 | ||||
| #define EMAC_ISR_BP			0x00800000 | ||||
| #define EMAC_ISR_RP			0x00400000 | ||||
| #define EMAC_ISR_SE			0x00200000 | ||||
| #define EMAC_ISR_ALE			0x00100000 | ||||
| #define EMAC_ISR_BFCS			0x00080000 | ||||
| #define EMAC_ISR_PTLE			0x00040000 | ||||
| #define EMAC_ISR_ORE			0x00020000 | ||||
| #define EMAC_ISR_IRE			0x00010000 | ||||
| #define EMAC_ISR_SQE			0x00000080 | ||||
| #define EMAC_ISR_TE			0x00000040 | ||||
| #define EMAC_ISR_MOS			0x00000002 | ||||
| #define EMAC_ISR_MOF			0x00000001 | ||||
| 
 | ||||
| /* EMACx_STACR */ | ||||
| #define EMAC_STACR_PHYD_MASK		0xffff | ||||
| #define EMAC_STACR_PHYD_SHIFT		16 | ||||
| #define EMAC_STACR_OC			0x00008000 | ||||
| #define EMAC_STACR_PHYE			0x00004000 | ||||
| #define EMAC_STACR_STAC_MASK		0x00003000 | ||||
| #define EMAC_STACR_STAC_READ		0x00001000 | ||||
| #define EMAC_STACR_STAC_WRITE		0x00002000 | ||||
| #define EMAC_STACR_OPBC_MASK		0x00000C00 | ||||
| #define EMAC_STACR_OPBC_50		0x00000000 | ||||
| #define EMAC_STACR_OPBC_66		0x00000400 | ||||
| #define EMAC_STACR_OPBC_83		0x00000800 | ||||
| #define EMAC_STACR_OPBC_100		0x00000C00 | ||||
| #define EMAC_STACR_OPBC(freq)		((freq) <= 50 ? EMAC_STACR_OPBC_50 : \ | ||||
| 					 (freq) <= 66 ? EMAC_STACR_OPBC_66 : \ | ||||
| 					 (freq) <= 83 ? EMAC_STACR_OPBC_83 : EMAC_STACR_OPBC_100) | ||||
| #define EMAC_STACR_BASE(opb)		EMAC_STACR_OPBC(opb) | ||||
| #define EMAC4_STACR_BASE(opb)		0x00000000 | ||||
| #define EMAC_STACR_PCDA_MASK		0x1f | ||||
| #define EMAC_STACR_PCDA_SHIFT		5 | ||||
| #define EMAC_STACR_PRA_MASK		0x1f | ||||
| #define EMACX_STACR_STAC_MASK		0x00003800 | ||||
| #define EMACX_STACR_STAC_READ		0x00001000 | ||||
| #define EMACX_STACR_STAC_WRITE		0x00000800 | ||||
| #define EMACX_STACR_STAC_IND_ADDR	0x00002000 | ||||
| #define EMACX_STACR_STAC_IND_READ	0x00003800 | ||||
| #define EMACX_STACR_STAC_IND_READINC	0x00003000 | ||||
| #define EMACX_STACR_STAC_IND_WRITE	0x00002800 | ||||
| 
 | ||||
| 
 | ||||
| /* EMACx_TRTR */ | ||||
| #define EMAC_TRTR_SHIFT_EMAC4		24 | ||||
| #define EMAC_TRTR_SHIFT		27 | ||||
| 
 | ||||
| /* EMAC specific TX descriptor control fields (write access) */ | ||||
| #define EMAC_TX_CTRL_GFCS		0x0200 | ||||
| #define EMAC_TX_CTRL_GP			0x0100 | ||||
| #define EMAC_TX_CTRL_ISA		0x0080 | ||||
| #define EMAC_TX_CTRL_RSA		0x0040 | ||||
| #define EMAC_TX_CTRL_IVT		0x0020 | ||||
| #define EMAC_TX_CTRL_RVT		0x0010 | ||||
| #define EMAC_TX_CTRL_TAH_CSUM		0x000e | ||||
| 
 | ||||
| /* EMAC specific TX descriptor status fields (read access) */ | ||||
| #define EMAC_TX_ST_BFCS			0x0200 | ||||
| #define EMAC_TX_ST_LCS			0x0080 | ||||
| #define EMAC_TX_ST_ED			0x0040 | ||||
| #define EMAC_TX_ST_EC			0x0020 | ||||
| #define EMAC_TX_ST_LC			0x0010 | ||||
| #define EMAC_TX_ST_MC			0x0008 | ||||
| #define EMAC_TX_ST_SC			0x0004 | ||||
| #define EMAC_TX_ST_UR			0x0002 | ||||
| #define EMAC_TX_ST_SQE			0x0001 | ||||
| #define EMAC_IS_BAD_TX			(EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \ | ||||
| 					 EMAC_TX_ST_EC | EMAC_TX_ST_LC | \ | ||||
| 					 EMAC_TX_ST_MC | EMAC_TX_ST_UR) | ||||
| #define EMAC_IS_BAD_TX_TAH		(EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \ | ||||
| 					 EMAC_TX_ST_EC | EMAC_TX_ST_LC) | ||||
| 
 | ||||
| /* EMAC specific RX descriptor status fields (read access) */ | ||||
| #define EMAC_RX_ST_OE			0x0200 | ||||
| #define EMAC_RX_ST_PP			0x0100 | ||||
| #define EMAC_RX_ST_BP			0x0080 | ||||
| #define EMAC_RX_ST_RP			0x0040 | ||||
| #define EMAC_RX_ST_SE			0x0020 | ||||
| #define EMAC_RX_ST_AE			0x0010 | ||||
| #define EMAC_RX_ST_BFCS			0x0008 | ||||
| #define EMAC_RX_ST_PTL			0x0004 | ||||
| #define EMAC_RX_ST_ORE			0x0002 | ||||
| #define EMAC_RX_ST_IRE			0x0001 | ||||
| #define EMAC_RX_TAH_BAD_CSUM		0x0003 | ||||
| #define EMAC_BAD_RX_MASK		(EMAC_RX_ST_OE | EMAC_RX_ST_BP | \ | ||||
| 					 EMAC_RX_ST_RP | EMAC_RX_ST_SE | \ | ||||
| 					 EMAC_RX_ST_AE | EMAC_RX_ST_BFCS | \ | ||||
| 					 EMAC_RX_ST_PTL | EMAC_RX_ST_ORE | \ | ||||
| 					 EMAC_RX_ST_IRE ) | ||||
| #endif /* __IBM_NEWEMAC_H */ | ||||
							
								
								
									
										794
									
								
								drivers/net/ethernet/ibm/emac/mal.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										794
									
								
								drivers/net/ethernet/ibm/emac/mal.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,794 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/mal.c | ||||
|  * | ||||
|  * Memory Access Layer (MAL) support | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Benjamin Herrenschmidt <benh@kernel.crashing.org>, | ||||
|  *      David Gibson <hermes@gibson.dropbear.id.au>, | ||||
|  * | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  *      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/delay.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/of_irq.h> | ||||
| 
 | ||||
| #include "core.h" | ||||
| #include <asm/dcr-regs.h> | ||||
| 
 | ||||
| static int mal_count; | ||||
| 
 | ||||
| int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "reg(%08x, %08x)" NL, | ||||
| 		commac->tx_chan_mask, commac->rx_chan_mask); | ||||
| 
 | ||||
| 	/* Don't let multiple commacs claim the same channel(s) */ | ||||
| 	if ((mal->tx_chan_mask & commac->tx_chan_mask) || | ||||
| 	    (mal->rx_chan_mask & commac->rx_chan_mask)) { | ||||
| 		spin_unlock_irqrestore(&mal->lock, flags); | ||||
| 		printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n", | ||||
| 		       mal->index); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 
 | ||||
| 	if (list_empty(&mal->list)) | ||||
| 		napi_enable(&mal->napi); | ||||
| 	mal->tx_chan_mask |= commac->tx_chan_mask; | ||||
| 	mal->rx_chan_mask |= commac->rx_chan_mask; | ||||
| 	list_add(&commac->list, &mal->list); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void mal_unregister_commac(struct mal_instance	*mal, | ||||
| 		struct mal_commac *commac) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "unreg(%08x, %08x)" NL, | ||||
| 		commac->tx_chan_mask, commac->rx_chan_mask); | ||||
| 
 | ||||
| 	mal->tx_chan_mask &= ~commac->tx_chan_mask; | ||||
| 	mal->rx_chan_mask &= ~commac->rx_chan_mask; | ||||
| 	list_del_init(&commac->list); | ||||
| 	if (list_empty(&mal->list)) | ||||
| 		napi_disable(&mal->napi); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| } | ||||
| 
 | ||||
| int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size) | ||||
| { | ||||
| 	BUG_ON(channel < 0 || channel >= mal->num_rx_chans || | ||||
| 	       size > MAL_MAX_RX_SIZE); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "set_rbcs(%d, %lu)" NL, channel, size); | ||||
| 
 | ||||
| 	if (size & 0xf) { | ||||
| 		printk(KERN_WARNING | ||||
| 		       "mal%d: incorrect RX size %lu for the channel %d\n", | ||||
| 		       mal->index, size, channel); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	set_mal_dcrn(mal, MAL_RCBS(channel), size >> 4); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int mal_tx_bd_offset(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	BUG_ON(channel < 0 || channel >= mal->num_tx_chans); | ||||
| 
 | ||||
| 	return channel * NUM_TX_BUFF; | ||||
| } | ||||
| 
 | ||||
| int mal_rx_bd_offset(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	BUG_ON(channel < 0 || channel >= mal->num_rx_chans); | ||||
| 	return mal->num_tx_chans * NUM_TX_BUFF + channel * NUM_RX_BUFF; | ||||
| } | ||||
| 
 | ||||
| void mal_enable_tx_channel(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "enable_tx(%d)" NL, channel); | ||||
| 
 | ||||
| 	set_mal_dcrn(mal, MAL_TXCASR, | ||||
| 		     get_mal_dcrn(mal, MAL_TXCASR) | MAL_CHAN_MASK(channel)); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| } | ||||
| 
 | ||||
| void mal_disable_tx_channel(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	set_mal_dcrn(mal, MAL_TXCARR, MAL_CHAN_MASK(channel)); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "disable_tx(%d)" NL, channel); | ||||
| } | ||||
| 
 | ||||
| void mal_enable_rx_channel(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple | ||||
| 	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value | ||||
| 	 * for the bitmask | ||||
| 	 */ | ||||
| 	if (!(channel % 8)) | ||||
| 		channel >>= 3; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "enable_rx(%d)" NL, channel); | ||||
| 
 | ||||
| 	set_mal_dcrn(mal, MAL_RXCASR, | ||||
| 		     get_mal_dcrn(mal, MAL_RXCASR) | MAL_CHAN_MASK(channel)); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| } | ||||
| 
 | ||||
| void mal_disable_rx_channel(struct mal_instance *mal, int channel) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * On some 4xx PPC's (e.g. 460EX/GT), the rx channel is a multiple | ||||
| 	 * of 8, but enabling in MAL_RXCASR needs the divided by 8 value | ||||
| 	 * for the bitmask | ||||
| 	 */ | ||||
| 	if (!(channel % 8)) | ||||
| 		channel >>= 3; | ||||
| 
 | ||||
| 	set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel)); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "disable_rx(%d)" NL, channel); | ||||
| } | ||||
| 
 | ||||
| void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "poll_add(%p)" NL, commac); | ||||
| 
 | ||||
| 	/* starts disabled */ | ||||
| 	set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags); | ||||
| 
 | ||||
| 	list_add_tail(&commac->poll_list, &mal->poll_list); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| } | ||||
| 
 | ||||
| void mal_poll_del(struct mal_instance *mal, struct mal_commac *commac) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "poll_del(%p)" NL, commac); | ||||
| 
 | ||||
| 	list_del(&commac->poll_list); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| } | ||||
| 
 | ||||
| /* synchronized by mal_poll() */ | ||||
| static inline void mal_enable_eob_irq(struct mal_instance *mal) | ||||
| { | ||||
| 	MAL_DBG2(mal, "enable_irq" NL); | ||||
| 
 | ||||
| 	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
 | ||||
| 	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) | MAL_CFG_EOPIE); | ||||
| } | ||||
| 
 | ||||
| /* synchronized by NAPI state */ | ||||
| static inline void mal_disable_eob_irq(struct mal_instance *mal) | ||||
| { | ||||
| 	// XXX might want to cache MAL_CFG as the DCR read can be slooooow
 | ||||
| 	set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) & ~MAL_CFG_EOPIE); | ||||
| 
 | ||||
| 	MAL_DBG2(mal, "disable_irq" NL); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_serr(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 
 | ||||
| 	u32 esr = get_mal_dcrn(mal, MAL_ESR); | ||||
| 
 | ||||
| 	/* Clear the error status register */ | ||||
| 	set_mal_dcrn(mal, MAL_ESR, esr); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "SERR %08x" NL, esr); | ||||
| 
 | ||||
| 	if (esr & MAL_ESR_EVB) { | ||||
| 		if (esr & MAL_ESR_DE) { | ||||
| 			/* We ignore Descriptor error,
 | ||||
| 			 * TXDE or RXDE interrupt will be generated anyway. | ||||
| 			 */ | ||||
| 			return IRQ_HANDLED; | ||||
| 		} | ||||
| 
 | ||||
| 		if (esr & MAL_ESR_PEIN) { | ||||
| 			/* PLB error, it's probably buggy hardware or
 | ||||
| 			 * incorrect physical address in BD (i.e. bug) | ||||
| 			 */ | ||||
| 			if (net_ratelimit()) | ||||
| 				printk(KERN_ERR | ||||
| 				       "mal%d: system error, " | ||||
| 				       "PLB (ESR = 0x%08x)\n", | ||||
| 				       mal->index, esr); | ||||
| 			return IRQ_HANDLED; | ||||
| 		} | ||||
| 
 | ||||
| 		/* OPB error, it's probably buggy hardware or incorrect
 | ||||
| 		 * EBC setup | ||||
| 		 */ | ||||
| 		if (net_ratelimit()) | ||||
| 			printk(KERN_ERR | ||||
| 			       "mal%d: system error, OPB (ESR = 0x%08x)\n", | ||||
| 			       mal->index, esr); | ||||
| 	} | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static inline void mal_schedule_poll(struct mal_instance *mal) | ||||
| { | ||||
| 	if (likely(napi_schedule_prep(&mal->napi))) { | ||||
| 		MAL_DBG2(mal, "schedule_poll" NL); | ||||
| 		spin_lock(&mal->lock); | ||||
| 		mal_disable_eob_irq(mal); | ||||
| 		spin_unlock(&mal->lock); | ||||
| 		__napi_schedule(&mal->napi); | ||||
| 	} else | ||||
| 		MAL_DBG2(mal, "already in poll" NL); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_txeob(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 
 | ||||
| 	u32 r = get_mal_dcrn(mal, MAL_TXEOBISR); | ||||
| 
 | ||||
| 	MAL_DBG2(mal, "txeob %08x" NL, r); | ||||
| 
 | ||||
| 	mal_schedule_poll(mal); | ||||
| 	set_mal_dcrn(mal, MAL_TXEOBISR, r); | ||||
| 
 | ||||
| #ifdef CONFIG_PPC_DCR_NATIVE | ||||
| 	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT)) | ||||
| 		mtdcri(SDR0, DCRN_SDR_ICINTSTAT, | ||||
| 				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX)); | ||||
| #endif | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_rxeob(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 
 | ||||
| 	u32 r = get_mal_dcrn(mal, MAL_RXEOBISR); | ||||
| 
 | ||||
| 	MAL_DBG2(mal, "rxeob %08x" NL, r); | ||||
| 
 | ||||
| 	mal_schedule_poll(mal); | ||||
| 	set_mal_dcrn(mal, MAL_RXEOBISR, r); | ||||
| 
 | ||||
| #ifdef CONFIG_PPC_DCR_NATIVE | ||||
| 	if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT)) | ||||
| 		mtdcri(SDR0, DCRN_SDR_ICINTSTAT, | ||||
| 				(mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX)); | ||||
| #endif | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_txde(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 
 | ||||
| 	u32 deir = get_mal_dcrn(mal, MAL_TXDEIR); | ||||
| 	set_mal_dcrn(mal, MAL_TXDEIR, deir); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "txde %08x" NL, deir); | ||||
| 
 | ||||
| 	if (net_ratelimit()) | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: TX descriptor error (TXDEIR = 0x%08x)\n", | ||||
| 		       mal->index, deir); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_rxde(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 	struct list_head *l; | ||||
| 
 | ||||
| 	u32 deir = get_mal_dcrn(mal, MAL_RXDEIR); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "rxde %08x" NL, deir); | ||||
| 
 | ||||
| 	list_for_each(l, &mal->list) { | ||||
| 		struct mal_commac *mc = list_entry(l, struct mal_commac, list); | ||||
| 		if (deir & mc->rx_chan_mask) { | ||||
| 			set_bit(MAL_COMMAC_RX_STOPPED, &mc->flags); | ||||
| 			mc->ops->rxde(mc->dev); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mal_schedule_poll(mal); | ||||
| 	set_mal_dcrn(mal, MAL_RXDEIR, deir); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mal_int(int irq, void *dev_instance) | ||||
| { | ||||
| 	struct mal_instance *mal = dev_instance; | ||||
| 	u32 esr = get_mal_dcrn(mal, MAL_ESR); | ||||
| 
 | ||||
| 	if (esr & MAL_ESR_EVB) { | ||||
| 		/* descriptor error */ | ||||
| 		if (esr & MAL_ESR_DE) { | ||||
| 			if (esr & MAL_ESR_CIDT) | ||||
| 				return mal_rxde(irq, dev_instance); | ||||
| 			else | ||||
| 				return mal_txde(irq, dev_instance); | ||||
| 		} else { /* SERR */ | ||||
| 			return mal_serr(irq, dev_instance); | ||||
| 		} | ||||
| 	} | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac) | ||||
| { | ||||
| 	/* Spinlock-type semantics: only one caller disable poll at a time */ | ||||
| 	while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags)) | ||||
| 		msleep(1); | ||||
| 
 | ||||
| 	/* Synchronize with the MAL NAPI poller */ | ||||
| 	napi_synchronize(&mal->napi); | ||||
| } | ||||
| 
 | ||||
| void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac) | ||||
| { | ||||
| 	smp_wmb(); | ||||
| 	clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags); | ||||
| 
 | ||||
| 	/* Feels better to trigger a poll here to catch up with events that
 | ||||
| 	 * may have happened on this channel while disabled. It will most | ||||
| 	 * probably be delayed until the next interrupt but that's mostly a | ||||
| 	 * non-issue in the context where this is called. | ||||
| 	 */ | ||||
| 	napi_schedule(&mal->napi); | ||||
| } | ||||
| 
 | ||||
| static int mal_poll(struct napi_struct *napi, int budget) | ||||
| { | ||||
| 	struct mal_instance *mal = container_of(napi, struct mal_instance, napi); | ||||
| 	struct list_head *l; | ||||
| 	int received = 0; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	MAL_DBG2(mal, "poll(%d)" NL, budget); | ||||
|  again: | ||||
| 	/* Process TX skbs */ | ||||
| 	list_for_each(l, &mal->poll_list) { | ||||
| 		struct mal_commac *mc = | ||||
| 			list_entry(l, struct mal_commac, poll_list); | ||||
| 		mc->ops->poll_tx(mc->dev); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Process RX skbs.
 | ||||
| 	 * | ||||
| 	 * We _might_ need something more smart here to enforce polling | ||||
| 	 * fairness. | ||||
| 	 */ | ||||
| 	list_for_each(l, &mal->poll_list) { | ||||
| 		struct mal_commac *mc = | ||||
| 			list_entry(l, struct mal_commac, poll_list); | ||||
| 		int n; | ||||
| 		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags))) | ||||
| 			continue; | ||||
| 		n = mc->ops->poll_rx(mc->dev, budget); | ||||
| 		if (n) { | ||||
| 			received += n; | ||||
| 			budget -= n; | ||||
| 			if (budget <= 0) | ||||
| 				goto more_work; // XXX What if this is the last one ?
 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* We need to disable IRQs to protect from RXDE IRQ here */ | ||||
| 	spin_lock_irqsave(&mal->lock, flags); | ||||
| 	__napi_complete(napi); | ||||
| 	mal_enable_eob_irq(mal); | ||||
| 	spin_unlock_irqrestore(&mal->lock, flags); | ||||
| 
 | ||||
| 	/* Check for "rotting" packet(s) */ | ||||
| 	list_for_each(l, &mal->poll_list) { | ||||
| 		struct mal_commac *mc = | ||||
| 			list_entry(l, struct mal_commac, poll_list); | ||||
| 		if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags))) | ||||
| 			continue; | ||||
| 		if (unlikely(mc->ops->peek_rx(mc->dev) || | ||||
| 			     test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) { | ||||
| 			MAL_DBG2(mal, "rotting packet" NL); | ||||
| 			if (!napi_reschedule(napi)) | ||||
| 				goto more_work; | ||||
| 
 | ||||
| 			spin_lock_irqsave(&mal->lock, flags); | ||||
| 			mal_disable_eob_irq(mal); | ||||
| 			spin_unlock_irqrestore(&mal->lock, flags); | ||||
| 			goto again; | ||||
| 		} | ||||
| 		mc->ops->poll_tx(mc->dev); | ||||
| 	} | ||||
| 
 | ||||
|  more_work: | ||||
| 	MAL_DBG2(mal, "poll() %d <- %d" NL, budget, received); | ||||
| 	return received; | ||||
| } | ||||
| 
 | ||||
| static void mal_reset(struct mal_instance *mal) | ||||
| { | ||||
| 	int n = 10; | ||||
| 
 | ||||
| 	MAL_DBG(mal, "reset" NL); | ||||
| 
 | ||||
| 	set_mal_dcrn(mal, MAL_CFG, MAL_CFG_SR); | ||||
| 
 | ||||
| 	/* Wait for reset to complete (1 system clock) */ | ||||
| 	while ((get_mal_dcrn(mal, MAL_CFG) & MAL_CFG_SR) && n) | ||||
| 		--n; | ||||
| 
 | ||||
| 	if (unlikely(!n)) | ||||
| 		printk(KERN_ERR "mal%d: reset timeout\n", mal->index); | ||||
| } | ||||
| 
 | ||||
| int mal_get_regs_len(struct mal_instance *mal) | ||||
| { | ||||
| 	return sizeof(struct emac_ethtool_regs_subhdr) + | ||||
| 	    sizeof(struct mal_regs); | ||||
| } | ||||
| 
 | ||||
| void *mal_dump_regs(struct mal_instance *mal, void *buf) | ||||
| { | ||||
| 	struct emac_ethtool_regs_subhdr *hdr = buf; | ||||
| 	struct mal_regs *regs = (struct mal_regs *)(hdr + 1); | ||||
| 	int i; | ||||
| 
 | ||||
| 	hdr->version = mal->version; | ||||
| 	hdr->index = mal->index; | ||||
| 
 | ||||
| 	regs->tx_count = mal->num_tx_chans; | ||||
| 	regs->rx_count = mal->num_rx_chans; | ||||
| 
 | ||||
| 	regs->cfg = get_mal_dcrn(mal, MAL_CFG); | ||||
| 	regs->esr = get_mal_dcrn(mal, MAL_ESR); | ||||
| 	regs->ier = get_mal_dcrn(mal, MAL_IER); | ||||
| 	regs->tx_casr = get_mal_dcrn(mal, MAL_TXCASR); | ||||
| 	regs->tx_carr = get_mal_dcrn(mal, MAL_TXCARR); | ||||
| 	regs->tx_eobisr = get_mal_dcrn(mal, MAL_TXEOBISR); | ||||
| 	regs->tx_deir = get_mal_dcrn(mal, MAL_TXDEIR); | ||||
| 	regs->rx_casr = get_mal_dcrn(mal, MAL_RXCASR); | ||||
| 	regs->rx_carr = get_mal_dcrn(mal, MAL_RXCARR); | ||||
| 	regs->rx_eobisr = get_mal_dcrn(mal, MAL_RXEOBISR); | ||||
| 	regs->rx_deir = get_mal_dcrn(mal, MAL_RXDEIR); | ||||
| 
 | ||||
| 	for (i = 0; i < regs->tx_count; ++i) | ||||
| 		regs->tx_ctpr[i] = get_mal_dcrn(mal, MAL_TXCTPR(i)); | ||||
| 
 | ||||
| 	for (i = 0; i < regs->rx_count; ++i) { | ||||
| 		regs->rx_ctpr[i] = get_mal_dcrn(mal, MAL_RXCTPR(i)); | ||||
| 		regs->rcbs[i] = get_mal_dcrn(mal, MAL_RCBS(i)); | ||||
| 	} | ||||
| 	return regs + 1; | ||||
| } | ||||
| 
 | ||||
| static int mal_probe(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct mal_instance *mal; | ||||
| 	int err = 0, i, bd_size; | ||||
| 	int index = mal_count++; | ||||
| 	unsigned int dcr_base; | ||||
| 	const u32 *prop; | ||||
| 	u32 cfg; | ||||
| 	unsigned long irqflags; | ||||
| 	irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde; | ||||
| 
 | ||||
| 	mal = kzalloc(sizeof(struct mal_instance), GFP_KERNEL); | ||||
| 	if (!mal) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	mal->index = index; | ||||
| 	mal->ofdev = ofdev; | ||||
| 	mal->version = of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal2") ? 2 : 1; | ||||
| 
 | ||||
| 	MAL_DBG(mal, "probe" NL); | ||||
| 
 | ||||
| 	prop = of_get_property(ofdev->dev.of_node, "num-tx-chans", NULL); | ||||
| 	if (prop == NULL) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: can't find MAL num-tx-chans property!\n", | ||||
| 		       index); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	mal->num_tx_chans = prop[0]; | ||||
| 
 | ||||
| 	prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL); | ||||
| 	if (prop == NULL) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: can't find MAL num-rx-chans property!\n", | ||||
| 		       index); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	mal->num_rx_chans = prop[0]; | ||||
| 
 | ||||
| 	dcr_base = dcr_resource_start(ofdev->dev.of_node, 0); | ||||
| 	if (dcr_base == 0) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: can't find DCR resource!\n", index); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 	mal->dcr_host = dcr_map(ofdev->dev.of_node, dcr_base, 0x100); | ||||
| 	if (!DCR_MAP_OK(mal->dcr_host)) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: failed to map DCRs !\n", index); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-405ez")) { | ||||
| #if defined(CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT) && \ | ||||
| 		defined(CONFIG_IBM_EMAC_MAL_COMMON_ERR) | ||||
| 		mal->features |= (MAL_FTR_CLEAR_ICINTSTAT | | ||||
| 				MAL_FTR_COMMON_ERR_INT); | ||||
| #else | ||||
| 		printk(KERN_ERR "%s: Support for 405EZ not enabled!\n", | ||||
| 				ofdev->dev.of_node->full_name); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail; | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| 	mal->txeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); | ||||
| 	mal->rxeob_irq = irq_of_parse_and_map(ofdev->dev.of_node, 1); | ||||
| 	mal->serr_irq = irq_of_parse_and_map(ofdev->dev.of_node, 2); | ||||
| 
 | ||||
| 	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) { | ||||
| 		mal->txde_irq = mal->rxde_irq = mal->serr_irq; | ||||
| 	} else { | ||||
| 		mal->txde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 3); | ||||
| 		mal->rxde_irq = irq_of_parse_and_map(ofdev->dev.of_node, 4); | ||||
| 	} | ||||
| 
 | ||||
| 	if (mal->txeob_irq == NO_IRQ || mal->rxeob_irq == NO_IRQ || | ||||
| 	    mal->serr_irq == NO_IRQ || mal->txde_irq == NO_IRQ || | ||||
| 	    mal->rxde_irq == NO_IRQ) { | ||||
| 		printk(KERN_ERR | ||||
| 		       "mal%d: failed to map interrupts !\n", index); | ||||
| 		err = -ENODEV; | ||||
| 		goto fail_unmap; | ||||
| 	} | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&mal->poll_list); | ||||
| 	INIT_LIST_HEAD(&mal->list); | ||||
| 	spin_lock_init(&mal->lock); | ||||
| 
 | ||||
| 	init_dummy_netdev(&mal->dummy_dev); | ||||
| 
 | ||||
| 	netif_napi_add(&mal->dummy_dev, &mal->napi, mal_poll, | ||||
| 		       CONFIG_IBM_EMAC_POLL_WEIGHT); | ||||
| 
 | ||||
| 	/* Load power-on reset defaults */ | ||||
| 	mal_reset(mal); | ||||
| 
 | ||||
| 	/* Set the MAL configuration register */ | ||||
| 	cfg = (mal->version == 2) ? MAL2_CFG_DEFAULT : MAL1_CFG_DEFAULT; | ||||
| 	cfg |= MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA; | ||||
| 
 | ||||
| 	/* Current Axon is not happy with priority being non-0, it can
 | ||||
| 	 * deadlock, fix it up here | ||||
| 	 */ | ||||
| 	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,mcmal-axon")) | ||||
| 		cfg &= ~(MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10); | ||||
| 
 | ||||
| 	/* Apply configuration */ | ||||
| 	set_mal_dcrn(mal, MAL_CFG, cfg); | ||||
| 
 | ||||
| 	/* Allocate space for BD rings */ | ||||
| 	BUG_ON(mal->num_tx_chans <= 0 || mal->num_tx_chans > 32); | ||||
| 	BUG_ON(mal->num_rx_chans <= 0 || mal->num_rx_chans > 32); | ||||
| 
 | ||||
| 	bd_size = sizeof(struct mal_descriptor) * | ||||
| 		(NUM_TX_BUFF * mal->num_tx_chans + | ||||
| 		 NUM_RX_BUFF * mal->num_rx_chans); | ||||
| 	mal->bd_virt = dma_zalloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma, | ||||
| 					   GFP_KERNEL); | ||||
| 	if (mal->bd_virt == NULL) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto fail_unmap; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < mal->num_tx_chans; ++i) | ||||
| 		set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma + | ||||
| 			     sizeof(struct mal_descriptor) * | ||||
| 			     mal_tx_bd_offset(mal, i)); | ||||
| 
 | ||||
| 	for (i = 0; i < mal->num_rx_chans; ++i) | ||||
| 		set_mal_dcrn(mal, MAL_RXCTPR(i), mal->bd_dma + | ||||
| 			     sizeof(struct mal_descriptor) * | ||||
| 			     mal_rx_bd_offset(mal, i)); | ||||
| 
 | ||||
| 	if (mal_has_feature(mal, MAL_FTR_COMMON_ERR_INT)) { | ||||
| 		irqflags = IRQF_SHARED; | ||||
| 		hdlr_serr = hdlr_txde = hdlr_rxde = mal_int; | ||||
| 	} else { | ||||
| 		irqflags = 0; | ||||
| 		hdlr_serr = mal_serr; | ||||
| 		hdlr_txde = mal_txde; | ||||
| 		hdlr_rxde = mal_rxde; | ||||
| 	} | ||||
| 
 | ||||
| 	err = request_irq(mal->serr_irq, hdlr_serr, irqflags, "MAL SERR", mal); | ||||
| 	if (err) | ||||
| 		goto fail2; | ||||
| 	err = request_irq(mal->txde_irq, hdlr_txde, irqflags, "MAL TX DE", mal); | ||||
| 	if (err) | ||||
| 		goto fail3; | ||||
| 	err = request_irq(mal->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal); | ||||
| 	if (err) | ||||
| 		goto fail4; | ||||
| 	err = request_irq(mal->rxde_irq, hdlr_rxde, irqflags, "MAL RX DE", mal); | ||||
| 	if (err) | ||||
| 		goto fail5; | ||||
| 	err = request_irq(mal->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal); | ||||
| 	if (err) | ||||
| 		goto fail6; | ||||
| 
 | ||||
| 	/* Enable all MAL SERR interrupt sources */ | ||||
| 	set_mal_dcrn(mal, MAL_IER, MAL_IER_EVENTS); | ||||
| 
 | ||||
| 	/* Enable EOB interrupt */ | ||||
| 	mal_enable_eob_irq(mal); | ||||
| 
 | ||||
| 	printk(KERN_INFO | ||||
| 	       "MAL v%d %s, %d TX channels, %d RX channels\n", | ||||
| 	       mal->version, ofdev->dev.of_node->full_name, | ||||
| 	       mal->num_tx_chans, mal->num_rx_chans); | ||||
| 
 | ||||
| 	/* Advertise this instance to the rest of the world */ | ||||
| 	wmb(); | ||||
| 	platform_set_drvdata(ofdev, mal); | ||||
| 
 | ||||
| 	mal_dbg_register(mal); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  fail6: | ||||
| 	free_irq(mal->rxde_irq, mal); | ||||
|  fail5: | ||||
| 	free_irq(mal->txeob_irq, mal); | ||||
|  fail4: | ||||
| 	free_irq(mal->txde_irq, mal); | ||||
|  fail3: | ||||
| 	free_irq(mal->serr_irq, mal); | ||||
|  fail2: | ||||
| 	dma_free_coherent(&ofdev->dev, bd_size, mal->bd_virt, mal->bd_dma); | ||||
|  fail_unmap: | ||||
| 	dcr_unmap(mal->dcr_host, 0x100); | ||||
|  fail: | ||||
| 	kfree(mal); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int mal_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct mal_instance *mal = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	MAL_DBG(mal, "remove" NL); | ||||
| 
 | ||||
| 	/* Synchronize with scheduled polling */ | ||||
| 	napi_disable(&mal->napi); | ||||
| 
 | ||||
| 	if (!list_empty(&mal->list)) | ||||
| 		/* This is *very* bad */ | ||||
| 		WARN(1, KERN_EMERG | ||||
| 		       "mal%d: commac list is not empty on remove!\n", | ||||
| 		       mal->index); | ||||
| 
 | ||||
| 	free_irq(mal->serr_irq, mal); | ||||
| 	free_irq(mal->txde_irq, mal); | ||||
| 	free_irq(mal->txeob_irq, mal); | ||||
| 	free_irq(mal->rxde_irq, mal); | ||||
| 	free_irq(mal->rxeob_irq, mal); | ||||
| 
 | ||||
| 	mal_reset(mal); | ||||
| 
 | ||||
| 	mal_dbg_unregister(mal); | ||||
| 
 | ||||
| 	dma_free_coherent(&ofdev->dev, | ||||
| 			  sizeof(struct mal_descriptor) * | ||||
| 			  (NUM_TX_BUFF * mal->num_tx_chans + | ||||
| 			   NUM_RX_BUFF * mal->num_rx_chans), mal->bd_virt, | ||||
| 			  mal->bd_dma); | ||||
| 	kfree(mal); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct of_device_id mal_platform_match[] = | ||||
| { | ||||
| 	{ | ||||
| 		.compatible	= "ibm,mcmal", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible	= "ibm,mcmal2", | ||||
| 	}, | ||||
| 	/* Backward compat */ | ||||
| 	{ | ||||
| 		.type		= "mcmal-dma", | ||||
| 		.compatible	= "ibm,mcmal", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.type		= "mcmal-dma", | ||||
| 		.compatible	= "ibm,mcmal2", | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver mal_of_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "mcmal", | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.of_match_table = mal_platform_match, | ||||
| 	}, | ||||
| 	.probe = mal_probe, | ||||
| 	.remove = mal_remove, | ||||
| }; | ||||
| 
 | ||||
| int __init mal_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&mal_of_driver); | ||||
| } | ||||
| 
 | ||||
| void mal_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&mal_of_driver); | ||||
| } | ||||
							
								
								
									
										312
									
								
								drivers/net/ethernet/ibm/emac/mal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								drivers/net/ethernet/ibm/emac/mal.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,312 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/mal.h | ||||
|  * | ||||
|  * Memory Access Layer (MAL) support | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  *      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. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __IBM_NEWEMAC_MAL_H | ||||
| #define __IBM_NEWEMAC_MAL_H | ||||
| 
 | ||||
| /*
 | ||||
|  * There are some variations on the MAL, we express them in this driver as | ||||
|  * MAL Version 1 and 2 though that doesn't match any IBM terminology. | ||||
|  * | ||||
|  * We call MAL 1 the version in 405GP, 405GPR, 405EP, 440EP, 440GR and | ||||
|  * NP405H. | ||||
|  * | ||||
|  * We call MAL 2 the version in 440GP, 440GX, 440SP, 440SPE and Axon | ||||
|  * | ||||
|  * The driver expects a "version" property in the emac node containing | ||||
|  * a number 1 or 2. New device-trees for EMAC capable platforms are thus | ||||
|  * required to include that when porting to arch/powerpc. | ||||
|  */ | ||||
| 
 | ||||
| /* MALx DCR registers */ | ||||
| #define	MAL_CFG			0x00 | ||||
| #define	  MAL_CFG_SR		0x80000000 | ||||
| #define   MAL_CFG_PLBB		0x00004000 | ||||
| #define   MAL_CFG_OPBBL		0x00000080 | ||||
| #define   MAL_CFG_EOPIE		0x00000004 | ||||
| #define   MAL_CFG_LEA		0x00000002 | ||||
| #define   MAL_CFG_SD		0x00000001 | ||||
| 
 | ||||
| /* MAL V1 CFG bits */ | ||||
| #define   MAL1_CFG_PLBP_MASK	0x00c00000 | ||||
| #define   MAL1_CFG_PLBP_10	0x00800000 | ||||
| #define   MAL1_CFG_GA		0x00200000 | ||||
| #define   MAL1_CFG_OA		0x00100000 | ||||
| #define   MAL1_CFG_PLBLE	0x00080000 | ||||
| #define   MAL1_CFG_PLBT_MASK	0x00078000 | ||||
| #define   MAL1_CFG_DEFAULT	(MAL1_CFG_PLBP_10 | MAL1_CFG_PLBT_MASK) | ||||
| 
 | ||||
| /* MAL V2 CFG bits */ | ||||
| #define   MAL2_CFG_RPP_MASK	0x00c00000 | ||||
| #define   MAL2_CFG_RPP_10	0x00800000 | ||||
| #define   MAL2_CFG_RMBS_MASK	0x00300000 | ||||
| #define   MAL2_CFG_WPP_MASK	0x000c0000 | ||||
| #define   MAL2_CFG_WPP_10	0x00080000 | ||||
| #define   MAL2_CFG_WMBS_MASK	0x00030000 | ||||
| #define   MAL2_CFG_PLBLE	0x00008000 | ||||
| #define   MAL2_CFG_DEFAULT	(MAL2_CFG_RMBS_MASK | MAL2_CFG_WMBS_MASK | \ | ||||
| 				 MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10) | ||||
| 
 | ||||
| #define MAL_ESR			0x01 | ||||
| #define   MAL_ESR_EVB		0x80000000 | ||||
| #define   MAL_ESR_CIDT		0x40000000 | ||||
| #define   MAL_ESR_CID_MASK	0x3e000000 | ||||
| #define   MAL_ESR_CID_SHIFT	25 | ||||
| #define   MAL_ESR_DE		0x00100000 | ||||
| #define   MAL_ESR_OTE		0x00040000 | ||||
| #define   MAL_ESR_OSE		0x00020000 | ||||
| #define   MAL_ESR_PEIN		0x00010000 | ||||
| #define   MAL_ESR_DEI		0x00000010 | ||||
| #define   MAL_ESR_OTEI		0x00000004 | ||||
| #define   MAL_ESR_OSEI		0x00000002 | ||||
| #define   MAL_ESR_PBEI		0x00000001 | ||||
| 
 | ||||
| /* MAL V1 ESR bits */ | ||||
| #define   MAL1_ESR_ONE		0x00080000 | ||||
| #define   MAL1_ESR_ONEI		0x00000008 | ||||
| 
 | ||||
| /* MAL V2 ESR bits */ | ||||
| #define   MAL2_ESR_PTE		0x00800000 | ||||
| #define   MAL2_ESR_PRE		0x00400000 | ||||
| #define   MAL2_ESR_PWE		0x00200000 | ||||
| #define   MAL2_ESR_PTEI		0x00000080 | ||||
| #define   MAL2_ESR_PREI		0x00000040 | ||||
| #define   MAL2_ESR_PWEI		0x00000020 | ||||
| 
 | ||||
| 
 | ||||
| #define MAL_IER			0x02 | ||||
| /* MAL IER bits */ | ||||
| #define   MAL_IER_DE		0x00000010 | ||||
| #define   MAL_IER_OTE		0x00000004 | ||||
| #define   MAL_IER_OE		0x00000002 | ||||
| #define   MAL_IER_PE		0x00000001 | ||||
| 
 | ||||
| /* PLB read/write/timeout errors */ | ||||
| #define   MAL_IER_PTE		0x00000080 | ||||
| #define   MAL_IER_PRE		0x00000040 | ||||
| #define   MAL_IER_PWE		0x00000020 | ||||
| 
 | ||||
| #define   MAL_IER_SOC_EVENTS	(MAL_IER_PTE | MAL_IER_PRE | MAL_IER_PWE) | ||||
| #define   MAL_IER_EVENTS	(MAL_IER_SOC_EVENTS | MAL_IER_DE | \ | ||||
| 				 MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE) | ||||
| 
 | ||||
| #define MAL_TXCASR		0x04 | ||||
| #define MAL_TXCARR		0x05 | ||||
| #define MAL_TXEOBISR		0x06 | ||||
| #define MAL_TXDEIR		0x07 | ||||
| #define MAL_RXCASR		0x10 | ||||
| #define MAL_RXCARR		0x11 | ||||
| #define MAL_RXEOBISR		0x12 | ||||
| #define MAL_RXDEIR		0x13 | ||||
| #define MAL_TXCTPR(n)		((n) + 0x20) | ||||
| #define MAL_RXCTPR(n)		((n) + 0x40) | ||||
| #define MAL_RCBS(n)		((n) + 0x60) | ||||
| 
 | ||||
| /* In reality MAL can handle TX buffers up to 4095 bytes long,
 | ||||
|  * but this isn't a good round number :) 		 --ebs | ||||
|  */ | ||||
| #define MAL_MAX_TX_SIZE		4080 | ||||
| #define MAL_MAX_RX_SIZE		4080 | ||||
| 
 | ||||
| static inline int mal_rx_size(int len) | ||||
| { | ||||
| 	len = (len + 0xf) & ~0xf; | ||||
| 	return len > MAL_MAX_RX_SIZE ? MAL_MAX_RX_SIZE : len; | ||||
| } | ||||
| 
 | ||||
| static inline int mal_tx_chunks(int len) | ||||
| { | ||||
| 	return (len + MAL_MAX_TX_SIZE - 1) / MAL_MAX_TX_SIZE; | ||||
| } | ||||
| 
 | ||||
| #define MAL_CHAN_MASK(n)	(0x80000000 >> (n)) | ||||
| 
 | ||||
| /* MAL Buffer Descriptor structure */ | ||||
| struct mal_descriptor { | ||||
| 	u16 ctrl;		/* MAL / Commac status control bits */ | ||||
| 	u16 data_len;		/* Max length is 4K-1 (12 bits)     */ | ||||
| 	u32 data_ptr;		/* pointer to actual data buffer    */ | ||||
| }; | ||||
| 
 | ||||
| /* the following defines are for the MadMAL status and control registers. */ | ||||
| /* MADMAL transmit and receive status/control bits  */ | ||||
| #define MAL_RX_CTRL_EMPTY	0x8000 | ||||
| #define MAL_RX_CTRL_WRAP	0x4000 | ||||
| #define MAL_RX_CTRL_CM		0x2000 | ||||
| #define MAL_RX_CTRL_LAST	0x1000 | ||||
| #define MAL_RX_CTRL_FIRST	0x0800 | ||||
| #define MAL_RX_CTRL_INTR	0x0400 | ||||
| #define MAL_RX_CTRL_SINGLE	(MAL_RX_CTRL_LAST | MAL_RX_CTRL_FIRST) | ||||
| #define MAL_IS_SINGLE_RX(ctrl)	(((ctrl) & MAL_RX_CTRL_SINGLE) == MAL_RX_CTRL_SINGLE) | ||||
| 
 | ||||
| #define MAL_TX_CTRL_READY	0x8000 | ||||
| #define MAL_TX_CTRL_WRAP	0x4000 | ||||
| #define MAL_TX_CTRL_CM		0x2000 | ||||
| #define MAL_TX_CTRL_LAST	0x1000 | ||||
| #define MAL_TX_CTRL_INTR	0x0400 | ||||
| 
 | ||||
| struct mal_commac_ops { | ||||
| 	void	(*poll_tx) (void *dev); | ||||
| 	int	(*poll_rx) (void *dev, int budget); | ||||
| 	int	(*peek_rx) (void *dev); | ||||
| 	void	(*rxde) (void *dev); | ||||
| }; | ||||
| 
 | ||||
| struct mal_commac { | ||||
| 	struct mal_commac_ops	*ops; | ||||
| 	void			*dev; | ||||
| 	struct list_head	poll_list; | ||||
| 	long       		flags; | ||||
| #define MAL_COMMAC_RX_STOPPED		0 | ||||
| #define MAL_COMMAC_POLL_DISABLED	1 | ||||
| 	u32			tx_chan_mask; | ||||
| 	u32			rx_chan_mask; | ||||
| 	struct list_head	list; | ||||
| }; | ||||
| 
 | ||||
| struct mal_instance { | ||||
| 	int			version; | ||||
| 	dcr_host_t		dcr_host; | ||||
| 
 | ||||
| 	int			num_tx_chans;	/* Number of TX channels */ | ||||
| 	int			num_rx_chans;	/* Number of RX channels */ | ||||
| 	int 			txeob_irq;	/* TX End Of Buffer IRQ  */ | ||||
| 	int 			rxeob_irq;	/* RX End Of Buffer IRQ  */ | ||||
| 	int			txde_irq;	/* TX Descriptor Error IRQ */ | ||||
| 	int			rxde_irq;	/* RX Descriptor Error IRQ */ | ||||
| 	int			serr_irq;	/* MAL System Error IRQ    */ | ||||
| 
 | ||||
| 	struct list_head	poll_list; | ||||
| 	struct napi_struct	napi; | ||||
| 
 | ||||
| 	struct list_head	list; | ||||
| 	u32			tx_chan_mask; | ||||
| 	u32			rx_chan_mask; | ||||
| 
 | ||||
| 	dma_addr_t		bd_dma; | ||||
| 	struct mal_descriptor	*bd_virt; | ||||
| 
 | ||||
| 	struct platform_device	*ofdev; | ||||
| 	int			index; | ||||
| 	spinlock_t		lock; | ||||
| 
 | ||||
| 	struct net_device	dummy_dev; | ||||
| 
 | ||||
| 	unsigned int features; | ||||
| }; | ||||
| 
 | ||||
| static inline u32 get_mal_dcrn(struct mal_instance *mal, int reg) | ||||
| { | ||||
| 	return dcr_read(mal->dcr_host, reg); | ||||
| } | ||||
| 
 | ||||
| static inline void set_mal_dcrn(struct mal_instance *mal, int reg, u32 val) | ||||
| { | ||||
| 	dcr_write(mal->dcr_host, reg, val); | ||||
| } | ||||
| 
 | ||||
| /* Features of various MAL implementations */ | ||||
| 
 | ||||
| /* Set if you have interrupt coalescing and you have to clear the SDR
 | ||||
|  * register for TXEOB and RXEOB interrupts to work | ||||
|  */ | ||||
| #define MAL_FTR_CLEAR_ICINTSTAT	0x00000001 | ||||
| 
 | ||||
| /* Set if your MAL has SERR, TXDE, and RXDE OR'd into a single UIC
 | ||||
|  * interrupt | ||||
|  */ | ||||
| #define MAL_FTR_COMMON_ERR_INT	0x00000002 | ||||
| 
 | ||||
| enum { | ||||
| 	MAL_FTRS_ALWAYS = 0, | ||||
| 
 | ||||
| 	MAL_FTRS_POSSIBLE = | ||||
| #ifdef CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT | ||||
| 		MAL_FTR_CLEAR_ICINTSTAT | | ||||
| #endif | ||||
| #ifdef CONFIG_IBM_EMAC_MAL_COMMON_ERR | ||||
| 		MAL_FTR_COMMON_ERR_INT | | ||||
| #endif | ||||
| 		0, | ||||
| }; | ||||
| 
 | ||||
| static inline int mal_has_feature(struct mal_instance *dev, | ||||
| 		unsigned long feature) | ||||
| { | ||||
| 	return (MAL_FTRS_ALWAYS & feature) || | ||||
| 		(MAL_FTRS_POSSIBLE & dev->features & feature); | ||||
| } | ||||
| 
 | ||||
| /* Register MAL devices */ | ||||
| int mal_init(void); | ||||
| void mal_exit(void); | ||||
| 
 | ||||
| int mal_register_commac(struct mal_instance *mal, | ||||
| 			struct mal_commac *commac); | ||||
| void mal_unregister_commac(struct mal_instance *mal, | ||||
| 			   struct mal_commac *commac); | ||||
| int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size); | ||||
| 
 | ||||
| /* Returns BD ring offset for a particular channel
 | ||||
|    (in 'struct mal_descriptor' elements) | ||||
| */ | ||||
| int mal_tx_bd_offset(struct mal_instance *mal, int channel); | ||||
| int mal_rx_bd_offset(struct mal_instance *mal, int channel); | ||||
| 
 | ||||
| void mal_enable_tx_channel(struct mal_instance *mal, int channel); | ||||
| void mal_disable_tx_channel(struct mal_instance *mal, int channel); | ||||
| void mal_enable_rx_channel(struct mal_instance *mal, int channel); | ||||
| void mal_disable_rx_channel(struct mal_instance *mal, int channel); | ||||
| 
 | ||||
| void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac); | ||||
| void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac); | ||||
| 
 | ||||
| /* Add/remove EMAC to/from MAL polling list */ | ||||
| void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac); | ||||
| void mal_poll_del(struct mal_instance *mal, struct mal_commac *commac); | ||||
| 
 | ||||
| /* Ethtool MAL registers */ | ||||
| struct mal_regs { | ||||
| 	u32 tx_count; | ||||
| 	u32 rx_count; | ||||
| 
 | ||||
| 	u32 cfg; | ||||
| 	u32 esr; | ||||
| 	u32 ier; | ||||
| 	u32 tx_casr; | ||||
| 	u32 tx_carr; | ||||
| 	u32 tx_eobisr; | ||||
| 	u32 tx_deir; | ||||
| 	u32 rx_casr; | ||||
| 	u32 rx_carr; | ||||
| 	u32 rx_eobisr; | ||||
| 	u32 rx_deir; | ||||
| 	u32 tx_ctpr[32]; | ||||
| 	u32 rx_ctpr[32]; | ||||
| 	u32 rcbs[32]; | ||||
| }; | ||||
| 
 | ||||
| int mal_get_regs_len(struct mal_instance *mal); | ||||
| void *mal_dump_regs(struct mal_instance *mal, void *buf); | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_MAL_H */ | ||||
							
								
								
									
										541
									
								
								drivers/net/ethernet/ibm/emac/phy.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										541
									
								
								drivers/net/ethernet/ibm/emac/phy.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,541 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/phy.c | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, PHY support. | ||||
|  * Borrowed from sungem_phy.c, though I only kept the generic MII | ||||
|  * driver for now. | ||||
|  * | ||||
|  * This file should be shared with other drivers or eventually | ||||
|  * merged as the "low level" part of miilib | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org) | ||||
|  * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net> | ||||
|  * | ||||
|  */ | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/types.h> | ||||
| #include <linux/netdevice.h> | ||||
| #include <linux/mii.h> | ||||
| #include <linux/ethtool.h> | ||||
| #include <linux/delay.h> | ||||
| 
 | ||||
| #include "emac.h" | ||||
| #include "phy.h" | ||||
| 
 | ||||
| #define phy_read _phy_read | ||||
| #define phy_write _phy_write | ||||
| 
 | ||||
| static inline int _phy_read(struct mii_phy *phy, int reg) | ||||
| { | ||||
| 	return phy->mdio_read(phy->dev, phy->address, reg); | ||||
| } | ||||
| 
 | ||||
| static inline void _phy_write(struct mii_phy *phy, int reg, int val) | ||||
| { | ||||
| 	phy->mdio_write(phy->dev, phy->address, reg, val); | ||||
| } | ||||
| 
 | ||||
| static inline int gpcs_phy_read(struct mii_phy *phy, int reg) | ||||
| { | ||||
| 	return phy->mdio_read(phy->dev, phy->gpcs_address, reg); | ||||
| } | ||||
| 
 | ||||
| static inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val) | ||||
| { | ||||
| 	phy->mdio_write(phy->dev, phy->gpcs_address, reg, val); | ||||
| } | ||||
| 
 | ||||
| int emac_mii_reset_phy(struct mii_phy *phy) | ||||
| { | ||||
| 	int val; | ||||
| 	int limit = 10000; | ||||
| 
 | ||||
| 	val = phy_read(phy, MII_BMCR); | ||||
| 	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); | ||||
| 	val |= BMCR_RESET; | ||||
| 	phy_write(phy, MII_BMCR, val); | ||||
| 
 | ||||
| 	udelay(300); | ||||
| 
 | ||||
| 	while (--limit) { | ||||
| 		val = phy_read(phy, MII_BMCR); | ||||
| 		if (val >= 0 && (val & BMCR_RESET) == 0) | ||||
| 			break; | ||||
| 		udelay(10); | ||||
| 	} | ||||
| 	if ((val & BMCR_ISOLATE) && limit > 0) | ||||
| 		phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE); | ||||
| 
 | ||||
| 	return limit <= 0; | ||||
| } | ||||
| 
 | ||||
| int emac_mii_reset_gpcs(struct mii_phy *phy) | ||||
| { | ||||
| 	int val; | ||||
| 	int limit = 10000; | ||||
| 
 | ||||
| 	val = gpcs_phy_read(phy, MII_BMCR); | ||||
| 	val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); | ||||
| 	val |= BMCR_RESET; | ||||
| 	gpcs_phy_write(phy, MII_BMCR, val); | ||||
| 
 | ||||
| 	udelay(300); | ||||
| 
 | ||||
| 	while (--limit) { | ||||
| 		val = gpcs_phy_read(phy, MII_BMCR); | ||||
| 		if (val >= 0 && (val & BMCR_RESET) == 0) | ||||
| 			break; | ||||
| 		udelay(10); | ||||
| 	} | ||||
| 	if ((val & BMCR_ISOLATE) && limit > 0) | ||||
| 		gpcs_phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE); | ||||
| 
 | ||||
| 	if (limit > 0 && phy->mode == PHY_MODE_SGMII) { | ||||
| 		/* Configure GPCS interface to recommended setting for SGMII */ | ||||
| 		gpcs_phy_write(phy, 0x04, 0x8120); /* AsymPause, FDX */ | ||||
| 		gpcs_phy_write(phy, 0x07, 0x2801); /* msg_pg, toggle */ | ||||
| 		gpcs_phy_write(phy, 0x00, 0x0140); /* 1Gbps, FDX     */ | ||||
| 	} | ||||
| 
 | ||||
| 	return limit <= 0; | ||||
| } | ||||
| 
 | ||||
| static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) | ||||
| { | ||||
| 	int ctl, adv; | ||||
| 
 | ||||
| 	phy->autoneg = AUTONEG_ENABLE; | ||||
| 	phy->speed = SPEED_10; | ||||
| 	phy->duplex = DUPLEX_HALF; | ||||
| 	phy->pause = phy->asym_pause = 0; | ||||
| 	phy->advertising = advertise; | ||||
| 
 | ||||
| 	ctl = phy_read(phy, MII_BMCR); | ||||
| 	if (ctl < 0) | ||||
| 		return ctl; | ||||
| 	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); | ||||
| 
 | ||||
| 	/* First clear the PHY */ | ||||
| 	phy_write(phy, MII_BMCR, ctl); | ||||
| 
 | ||||
| 	/* Setup standard advertise */ | ||||
| 	adv = phy_read(phy, MII_ADVERTISE); | ||||
| 	if (adv < 0) | ||||
| 		return adv; | ||||
| 	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | | ||||
| 		 ADVERTISE_PAUSE_ASYM); | ||||
| 	if (advertise & ADVERTISED_10baseT_Half) | ||||
| 		adv |= ADVERTISE_10HALF; | ||||
| 	if (advertise & ADVERTISED_10baseT_Full) | ||||
| 		adv |= ADVERTISE_10FULL; | ||||
| 	if (advertise & ADVERTISED_100baseT_Half) | ||||
| 		adv |= ADVERTISE_100HALF; | ||||
| 	if (advertise & ADVERTISED_100baseT_Full) | ||||
| 		adv |= ADVERTISE_100FULL; | ||||
| 	if (advertise & ADVERTISED_Pause) | ||||
| 		adv |= ADVERTISE_PAUSE_CAP; | ||||
| 	if (advertise & ADVERTISED_Asym_Pause) | ||||
| 		adv |= ADVERTISE_PAUSE_ASYM; | ||||
| 	phy_write(phy, MII_ADVERTISE, adv); | ||||
| 
 | ||||
| 	if (phy->features & | ||||
| 	    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { | ||||
| 		adv = phy_read(phy, MII_CTRL1000); | ||||
| 		if (adv < 0) | ||||
| 			return adv; | ||||
| 		adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); | ||||
| 		if (advertise & ADVERTISED_1000baseT_Full) | ||||
| 			adv |= ADVERTISE_1000FULL; | ||||
| 		if (advertise & ADVERTISED_1000baseT_Half) | ||||
| 			adv |= ADVERTISE_1000HALF; | ||||
| 		phy_write(phy, MII_CTRL1000, adv); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Start/Restart aneg */ | ||||
| 	ctl = phy_read(phy, MII_BMCR); | ||||
| 	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); | ||||
| 	phy_write(phy, MII_BMCR, ctl); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) | ||||
| { | ||||
| 	int ctl; | ||||
| 
 | ||||
| 	phy->autoneg = AUTONEG_DISABLE; | ||||
| 	phy->speed = speed; | ||||
| 	phy->duplex = fd; | ||||
| 	phy->pause = phy->asym_pause = 0; | ||||
| 
 | ||||
| 	ctl = phy_read(phy, MII_BMCR); | ||||
| 	if (ctl < 0) | ||||
| 		return ctl; | ||||
| 	ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); | ||||
| 
 | ||||
| 	/* First clear the PHY */ | ||||
| 	phy_write(phy, MII_BMCR, ctl | BMCR_RESET); | ||||
| 
 | ||||
| 	/* Select speed & duplex */ | ||||
| 	switch (speed) { | ||||
| 	case SPEED_10: | ||||
| 		break; | ||||
| 	case SPEED_100: | ||||
| 		ctl |= BMCR_SPEED100; | ||||
| 		break; | ||||
| 	case SPEED_1000: | ||||
| 		ctl |= BMCR_SPEED1000; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (fd == DUPLEX_FULL) | ||||
| 		ctl |= BMCR_FULLDPLX; | ||||
| 	phy_write(phy, MII_BMCR, ctl); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int genmii_poll_link(struct mii_phy *phy) | ||||
| { | ||||
| 	int status; | ||||
| 
 | ||||
| 	/* Clear latched value with dummy read */ | ||||
| 	phy_read(phy, MII_BMSR); | ||||
| 	status = phy_read(phy, MII_BMSR); | ||||
| 	if (status < 0 || (status & BMSR_LSTATUS) == 0) | ||||
| 		return 0; | ||||
| 	if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE)) | ||||
| 		return 0; | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int genmii_read_link(struct mii_phy *phy) | ||||
| { | ||||
| 	if (phy->autoneg == AUTONEG_ENABLE) { | ||||
| 		int glpa = 0; | ||||
| 		int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE); | ||||
| 		if (lpa < 0) | ||||
| 			return lpa; | ||||
| 
 | ||||
| 		if (phy->features & | ||||
| 		    (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { | ||||
| 			int adv = phy_read(phy, MII_CTRL1000); | ||||
| 			glpa = phy_read(phy, MII_STAT1000); | ||||
| 
 | ||||
| 			if (glpa < 0 || adv < 0) | ||||
| 				return adv; | ||||
| 
 | ||||
| 			glpa &= adv << 2; | ||||
| 		} | ||||
| 
 | ||||
| 		phy->speed = SPEED_10; | ||||
| 		phy->duplex = DUPLEX_HALF; | ||||
| 		phy->pause = phy->asym_pause = 0; | ||||
| 
 | ||||
| 		if (glpa & (LPA_1000FULL | LPA_1000HALF)) { | ||||
| 			phy->speed = SPEED_1000; | ||||
| 			if (glpa & LPA_1000FULL) | ||||
| 				phy->duplex = DUPLEX_FULL; | ||||
| 		} else if (lpa & (LPA_100FULL | LPA_100HALF)) { | ||||
| 			phy->speed = SPEED_100; | ||||
| 			if (lpa & LPA_100FULL) | ||||
| 				phy->duplex = DUPLEX_FULL; | ||||
| 		} else if (lpa & LPA_10FULL) | ||||
| 			phy->duplex = DUPLEX_FULL; | ||||
| 
 | ||||
| 		if (phy->duplex == DUPLEX_FULL) { | ||||
| 			phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; | ||||
| 			phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		int bmcr = phy_read(phy, MII_BMCR); | ||||
| 		if (bmcr < 0) | ||||
| 			return bmcr; | ||||
| 
 | ||||
| 		if (bmcr & BMCR_FULLDPLX) | ||||
| 			phy->duplex = DUPLEX_FULL; | ||||
| 		else | ||||
| 			phy->duplex = DUPLEX_HALF; | ||||
| 		if (bmcr & BMCR_SPEED1000) | ||||
| 			phy->speed = SPEED_1000; | ||||
| 		else if (bmcr & BMCR_SPEED100) | ||||
| 			phy->speed = SPEED_100; | ||||
| 		else | ||||
| 			phy->speed = SPEED_10; | ||||
| 
 | ||||
| 		phy->pause = phy->asym_pause = 0; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Generic implementation for most 10/100/1000 PHYs */ | ||||
| static struct mii_phy_ops generic_phy_ops = { | ||||
| 	.setup_aneg	= genmii_setup_aneg, | ||||
| 	.setup_forced	= genmii_setup_forced, | ||||
| 	.poll_link	= genmii_poll_link, | ||||
| 	.read_link	= genmii_read_link | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def genmii_phy_def = { | ||||
| 	.phy_id		= 0x00000000, | ||||
| 	.phy_id_mask	= 0x00000000, | ||||
| 	.name		= "Generic MII", | ||||
| 	.ops		= &generic_phy_ops | ||||
| }; | ||||
| 
 | ||||
| /* CIS8201 */ | ||||
| #define MII_CIS8201_10BTCSR	0x16 | ||||
| #define  TENBTCSR_ECHO_DISABLE	0x2000 | ||||
| #define MII_CIS8201_EPCR	0x17 | ||||
| #define  EPCR_MODE_MASK		0x3000 | ||||
| #define  EPCR_GMII_MODE		0x0000 | ||||
| #define  EPCR_RGMII_MODE	0x1000 | ||||
| #define  EPCR_TBI_MODE		0x2000 | ||||
| #define  EPCR_RTBI_MODE		0x3000 | ||||
| #define MII_CIS8201_ACSR	0x1c | ||||
| #define  ACSR_PIN_PRIO_SELECT	0x0004 | ||||
| 
 | ||||
| static int cis8201_init(struct mii_phy *phy) | ||||
| { | ||||
| 	int epcr; | ||||
| 
 | ||||
| 	epcr = phy_read(phy, MII_CIS8201_EPCR); | ||||
| 	if (epcr < 0) | ||||
| 		return epcr; | ||||
| 
 | ||||
| 	epcr &= ~EPCR_MODE_MASK; | ||||
| 
 | ||||
| 	switch (phy->mode) { | ||||
| 	case PHY_MODE_TBI: | ||||
| 		epcr |= EPCR_TBI_MODE; | ||||
| 		break; | ||||
| 	case PHY_MODE_RTBI: | ||||
| 		epcr |= EPCR_RTBI_MODE; | ||||
| 		break; | ||||
| 	case PHY_MODE_GMII: | ||||
| 		epcr |= EPCR_GMII_MODE; | ||||
| 		break; | ||||
| 	case PHY_MODE_RGMII: | ||||
| 	default: | ||||
| 		epcr |= EPCR_RGMII_MODE; | ||||
| 	} | ||||
| 
 | ||||
| 	phy_write(phy, MII_CIS8201_EPCR, epcr); | ||||
| 
 | ||||
| 	/* MII regs override strap pins */ | ||||
| 	phy_write(phy, MII_CIS8201_ACSR, | ||||
| 		  phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT); | ||||
| 
 | ||||
| 	/* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */ | ||||
| 	phy_write(phy, MII_CIS8201_10BTCSR, | ||||
| 		  phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct mii_phy_ops cis8201_phy_ops = { | ||||
| 	.init		= cis8201_init, | ||||
| 	.setup_aneg	= genmii_setup_aneg, | ||||
| 	.setup_forced	= genmii_setup_forced, | ||||
| 	.poll_link	= genmii_poll_link, | ||||
| 	.read_link	= genmii_read_link | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def cis8201_phy_def = { | ||||
| 	.phy_id		= 0x000fc410, | ||||
| 	.phy_id_mask	= 0x000ffff0, | ||||
| 	.name		= "CIS8201 Gigabit Ethernet", | ||||
| 	.ops		= &cis8201_phy_ops | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def bcm5248_phy_def = { | ||||
| 
 | ||||
| 	.phy_id		= 0x0143bc00, | ||||
| 	.phy_id_mask	= 0x0ffffff0, | ||||
| 	.name		= "BCM5248 10/100 SMII Ethernet", | ||||
| 	.ops		= &generic_phy_ops | ||||
| }; | ||||
| 
 | ||||
| static int m88e1111_init(struct mii_phy *phy) | ||||
| { | ||||
| 	pr_debug("%s: Marvell 88E1111 Ethernet\n", __func__); | ||||
| 	phy_write(phy, 0x14, 0x0ce3); | ||||
| 	phy_write(phy, 0x18, 0x4101); | ||||
| 	phy_write(phy, 0x09, 0x0e00); | ||||
| 	phy_write(phy, 0x04, 0x01e1); | ||||
| 	phy_write(phy, 0x00, 0x9140); | ||||
| 	phy_write(phy, 0x00, 0x1140); | ||||
| 
 | ||||
| 	return  0; | ||||
| } | ||||
| 
 | ||||
| static int m88e1112_init(struct mii_phy *phy) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Marvell 88E1112 PHY needs to have the SGMII MAC | ||||
| 	 * interace (page 2) properly configured to | ||||
| 	 * communicate with the 460EX/GT GPCS interface. | ||||
| 	 */ | ||||
| 
 | ||||
| 	u16 reg_short; | ||||
| 
 | ||||
| 	pr_debug("%s: Marvell 88E1112 Ethernet\n", __func__); | ||||
| 
 | ||||
| 	/* Set access to Page 2 */ | ||||
| 	phy_write(phy, 0x16, 0x0002); | ||||
| 
 | ||||
| 	phy_write(phy, 0x00, 0x0040); /* 1Gbps */ | ||||
| 	reg_short = (u16)(phy_read(phy, 0x1a)); | ||||
| 	reg_short |= 0x8000; /* bypass Auto-Negotiation */ | ||||
| 	phy_write(phy, 0x1a, reg_short); | ||||
| 	emac_mii_reset_phy(phy); /* reset MAC interface */ | ||||
| 
 | ||||
| 	/* Reset access to Page 0 */ | ||||
| 	phy_write(phy, 0x16, 0x0000); | ||||
| 
 | ||||
| 	return  0; | ||||
| } | ||||
| 
 | ||||
| static int et1011c_init(struct mii_phy *phy) | ||||
| { | ||||
| 	u16 reg_short; | ||||
| 
 | ||||
| 	reg_short = (u16)(phy_read(phy, 0x16)); | ||||
| 	reg_short &= ~(0x7); | ||||
| 	reg_short |= 0x6;	/* RGMII Trace Delay*/ | ||||
| 	phy_write(phy, 0x16, reg_short); | ||||
| 
 | ||||
| 	reg_short = (u16)(phy_read(phy, 0x17)); | ||||
| 	reg_short &= ~(0x40); | ||||
| 	phy_write(phy, 0x17, reg_short); | ||||
| 
 | ||||
| 	phy_write(phy, 0x1c, 0x74f0); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct mii_phy_ops et1011c_phy_ops = { | ||||
| 	.init		= et1011c_init, | ||||
| 	.setup_aneg	= genmii_setup_aneg, | ||||
| 	.setup_forced	= genmii_setup_forced, | ||||
| 	.poll_link	= genmii_poll_link, | ||||
| 	.read_link	= genmii_read_link | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def et1011c_phy_def = { | ||||
| 	.phy_id		= 0x0282f000, | ||||
| 	.phy_id_mask	= 0x0fffff00, | ||||
| 	.name		= "ET1011C Gigabit Ethernet", | ||||
| 	.ops		= &et1011c_phy_ops | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static struct mii_phy_ops m88e1111_phy_ops = { | ||||
| 	.init		= m88e1111_init, | ||||
| 	.setup_aneg	= genmii_setup_aneg, | ||||
| 	.setup_forced	= genmii_setup_forced, | ||||
| 	.poll_link	= genmii_poll_link, | ||||
| 	.read_link	= genmii_read_link | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def m88e1111_phy_def = { | ||||
| 
 | ||||
| 	.phy_id		= 0x01410CC0, | ||||
| 	.phy_id_mask	= 0x0ffffff0, | ||||
| 	.name		= "Marvell 88E1111 Ethernet", | ||||
| 	.ops		= &m88e1111_phy_ops, | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_ops m88e1112_phy_ops = { | ||||
| 	.init		= m88e1112_init, | ||||
| 	.setup_aneg	= genmii_setup_aneg, | ||||
| 	.setup_forced	= genmii_setup_forced, | ||||
| 	.poll_link	= genmii_poll_link, | ||||
| 	.read_link	= genmii_read_link | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def m88e1112_phy_def = { | ||||
| 	.phy_id		= 0x01410C90, | ||||
| 	.phy_id_mask	= 0x0ffffff0, | ||||
| 	.name		= "Marvell 88E1112 Ethernet", | ||||
| 	.ops		= &m88e1112_phy_ops, | ||||
| }; | ||||
| 
 | ||||
| static struct mii_phy_def *mii_phy_table[] = { | ||||
| 	&et1011c_phy_def, | ||||
| 	&cis8201_phy_def, | ||||
| 	&bcm5248_phy_def, | ||||
| 	&m88e1111_phy_def, | ||||
| 	&m88e1112_phy_def, | ||||
| 	&genmii_phy_def, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| int emac_mii_phy_probe(struct mii_phy *phy, int address) | ||||
| { | ||||
| 	struct mii_phy_def *def; | ||||
| 	int i; | ||||
| 	u32 id; | ||||
| 
 | ||||
| 	phy->autoneg = AUTONEG_DISABLE; | ||||
| 	phy->advertising = 0; | ||||
| 	phy->address = address; | ||||
| 	phy->speed = SPEED_10; | ||||
| 	phy->duplex = DUPLEX_HALF; | ||||
| 	phy->pause = phy->asym_pause = 0; | ||||
| 
 | ||||
| 	/* Take PHY out of isolate mode and reset it. */ | ||||
| 	if (emac_mii_reset_phy(phy)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	/* Read ID and find matching entry */ | ||||
| 	id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2); | ||||
| 	for (i = 0; (def = mii_phy_table[i]) != NULL; i++) | ||||
| 		if ((id & def->phy_id_mask) == def->phy_id) | ||||
| 			break; | ||||
| 	/* Should never be NULL (we have a generic entry), but... */ | ||||
| 	if (!def) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	phy->def = def; | ||||
| 
 | ||||
| 	/* Determine PHY features if needed */ | ||||
| 	phy->features = def->features; | ||||
| 	if (!phy->features) { | ||||
| 		u16 bmsr = phy_read(phy, MII_BMSR); | ||||
| 		if (bmsr & BMSR_ANEGCAPABLE) | ||||
| 			phy->features |= SUPPORTED_Autoneg; | ||||
| 		if (bmsr & BMSR_10HALF) | ||||
| 			phy->features |= SUPPORTED_10baseT_Half; | ||||
| 		if (bmsr & BMSR_10FULL) | ||||
| 			phy->features |= SUPPORTED_10baseT_Full; | ||||
| 		if (bmsr & BMSR_100HALF) | ||||
| 			phy->features |= SUPPORTED_100baseT_Half; | ||||
| 		if (bmsr & BMSR_100FULL) | ||||
| 			phy->features |= SUPPORTED_100baseT_Full; | ||||
| 		if (bmsr & BMSR_ESTATEN) { | ||||
| 			u16 esr = phy_read(phy, MII_ESTATUS); | ||||
| 			if (esr & ESTATUS_1000_TFULL) | ||||
| 				phy->features |= SUPPORTED_1000baseT_Full; | ||||
| 			if (esr & ESTATUS_1000_THALF) | ||||
| 				phy->features |= SUPPORTED_1000baseT_Half; | ||||
| 		} | ||||
| 		phy->features |= SUPPORTED_MII; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Setup default advertising */ | ||||
| 	phy->advertising = phy->features; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										87
									
								
								drivers/net/ethernet/ibm/emac/phy.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								drivers/net/ethernet/ibm/emac/phy.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/phy.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, PHY support | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Benjamin Herrenschmidt <benh@kernel.crashing.org> | ||||
|  * February 2003 | ||||
|  * | ||||
|  * Minor additions by Eugene Surovegin <ebs@ebshome.net>, 2004 | ||||
|  * | ||||
|  * 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. | ||||
|  * | ||||
|  * This file basically duplicates sungem_phy.{c,h} with different PHYs | ||||
|  * supported. I'm looking into merging that in a single mii layer more | ||||
|  * flexible than mii.c | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __IBM_NEWEMAC_PHY_H | ||||
| #define __IBM_NEWEMAC_PHY_H | ||||
| 
 | ||||
| struct mii_phy; | ||||
| 
 | ||||
| /* Operations supported by any kind of PHY */ | ||||
| struct mii_phy_ops { | ||||
| 	int (*init) (struct mii_phy * phy); | ||||
| 	int (*suspend) (struct mii_phy * phy, int wol_options); | ||||
| 	int (*setup_aneg) (struct mii_phy * phy, u32 advertise); | ||||
| 	int (*setup_forced) (struct mii_phy * phy, int speed, int fd); | ||||
| 	int (*poll_link) (struct mii_phy * phy); | ||||
| 	int (*read_link) (struct mii_phy * phy); | ||||
| }; | ||||
| 
 | ||||
| /* Structure used to statically define an mii/gii based PHY */ | ||||
| struct mii_phy_def { | ||||
| 	u32 phy_id;		/* Concatenated ID1 << 16 | ID2 */ | ||||
| 	u32 phy_id_mask;	/* Significant bits */ | ||||
| 	u32 features;		/* Ethtool SUPPORTED_* defines or
 | ||||
| 				   0 for autodetect */ | ||||
| 	int magic_aneg;		/* Autoneg does all speed test for us */ | ||||
| 	const char *name; | ||||
| 	const struct mii_phy_ops *ops; | ||||
| }; | ||||
| 
 | ||||
| /* An instance of a PHY, partially borrowed from mii_if_info */ | ||||
| struct mii_phy { | ||||
| 	struct mii_phy_def *def; | ||||
| 	u32 advertising;	/* Ethtool ADVERTISED_* defines */ | ||||
| 	u32 features;		/* Copied from mii_phy_def.features
 | ||||
| 				   or determined automaticaly */ | ||||
| 	int address;		/* PHY address */ | ||||
| 	int mode;		/* PHY mode */ | ||||
| 	int gpcs_address;	/* GPCS PHY address */ | ||||
| 
 | ||||
| 	/* 1: autoneg enabled, 0: disabled */ | ||||
| 	int autoneg; | ||||
| 
 | ||||
| 	/* forced speed & duplex (no autoneg)
 | ||||
| 	 * partner speed & duplex & pause (autoneg) | ||||
| 	 */ | ||||
| 	int speed; | ||||
| 	int duplex; | ||||
| 	int pause; | ||||
| 	int asym_pause; | ||||
| 
 | ||||
| 	/* Provided by host chip */ | ||||
| 	struct net_device *dev; | ||||
| 	int (*mdio_read) (struct net_device * dev, int addr, int reg); | ||||
| 	void (*mdio_write) (struct net_device * dev, int addr, int reg, | ||||
| 			    int val); | ||||
| }; | ||||
| 
 | ||||
| /* Pass in a struct mii_phy with dev, mdio_read and mdio_write
 | ||||
|  * filled, the remaining fields will be filled on return | ||||
|  */ | ||||
| int emac_mii_phy_probe(struct mii_phy *phy, int address); | ||||
| int emac_mii_reset_phy(struct mii_phy *phy); | ||||
| int emac_mii_reset_gpcs(struct mii_phy *phy); | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_PHY_H */ | ||||
							
								
								
									
										337
									
								
								drivers/net/ethernet/ibm/emac/rgmii.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								drivers/net/ethernet/ibm/emac/rgmii.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,337 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/rgmii.c | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  * 	Matt Porter <mporter@kernel.crashing.org> | ||||
|  * 	Copyright 2004 MontaVista Software, 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/slab.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/ethtool.h> | ||||
| #include <linux/of_address.h> | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| #include "emac.h" | ||||
| #include "debug.h" | ||||
| 
 | ||||
| // XXX FIXME: Axon seems to support a subset of the RGMII, we
 | ||||
| // thus need to take that into account and possibly change some
 | ||||
| // of the bit settings below that don't seem to quite match the
 | ||||
| // AXON spec
 | ||||
| 
 | ||||
| /* RGMIIx_FER */ | ||||
| #define RGMII_FER_MASK(idx)	(0x7 << ((idx) * 4)) | ||||
| #define RGMII_FER_RTBI(idx)	(0x4 << ((idx) * 4)) | ||||
| #define RGMII_FER_RGMII(idx)	(0x5 << ((idx) * 4)) | ||||
| #define RGMII_FER_TBI(idx)	(0x6 << ((idx) * 4)) | ||||
| #define RGMII_FER_GMII(idx)	(0x7 << ((idx) * 4)) | ||||
| #define RGMII_FER_MII(idx)	RGMII_FER_GMII(idx) | ||||
| 
 | ||||
| /* RGMIIx_SSR */ | ||||
| #define RGMII_SSR_MASK(idx)	(0x7 << ((idx) * 8)) | ||||
| #define RGMII_SSR_10(idx)	(0x1 << ((idx) * 8)) | ||||
| #define RGMII_SSR_100(idx)	(0x2 << ((idx) * 8)) | ||||
| #define RGMII_SSR_1000(idx)	(0x4 << ((idx) * 8)) | ||||
| 
 | ||||
| /* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */ | ||||
| static inline int rgmii_valid_mode(int phy_mode) | ||||
| { | ||||
| 	return  phy_mode == PHY_MODE_GMII || | ||||
| 		phy_mode == PHY_MODE_MII || | ||||
| 		phy_mode == PHY_MODE_RGMII || | ||||
| 		phy_mode == PHY_MODE_TBI || | ||||
| 		phy_mode == PHY_MODE_RTBI; | ||||
| } | ||||
| 
 | ||||
| static inline const char *rgmii_mode_name(int mode) | ||||
| { | ||||
| 	switch (mode) { | ||||
| 	case PHY_MODE_RGMII: | ||||
| 		return "RGMII"; | ||||
| 	case PHY_MODE_TBI: | ||||
| 		return "TBI"; | ||||
| 	case PHY_MODE_GMII: | ||||
| 		return "GMII"; | ||||
| 	case PHY_MODE_MII: | ||||
| 		return "MII"; | ||||
| 	case PHY_MODE_RTBI: | ||||
| 		return "RTBI"; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u32 rgmii_mode_mask(int mode, int input) | ||||
| { | ||||
| 	switch (mode) { | ||||
| 	case PHY_MODE_RGMII: | ||||
| 		return RGMII_FER_RGMII(input); | ||||
| 	case PHY_MODE_TBI: | ||||
| 		return RGMII_FER_TBI(input); | ||||
| 	case PHY_MODE_GMII: | ||||
| 		return RGMII_FER_GMII(input); | ||||
| 	case PHY_MODE_MII: | ||||
| 		return RGMII_FER_MII(input); | ||||
| 	case PHY_MODE_RTBI: | ||||
| 		return RGMII_FER_RTBI(input); | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int rgmii_attach(struct platform_device *ofdev, int input, int mode) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct rgmii_regs __iomem *p = dev->base; | ||||
| 
 | ||||
| 	RGMII_DBG(dev, "attach(%d)" NL, input); | ||||
| 
 | ||||
| 	/* Check if we need to attach to a RGMII */ | ||||
| 	if (input < 0 || !rgmii_valid_mode(mode)) { | ||||
| 		printk(KERN_ERR "%s: unsupported settings !\n", | ||||
| 		       ofdev->dev.of_node->full_name); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	/* Enable this input */ | ||||
| 	out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); | ||||
| 
 | ||||
| 	printk(KERN_NOTICE "%s: input %d in %s mode\n", | ||||
| 	       ofdev->dev.of_node->full_name, input, rgmii_mode_name(mode)); | ||||
| 
 | ||||
| 	++dev->users; | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct rgmii_regs __iomem *p = dev->base; | ||||
| 	u32 ssr; | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	ssr = in_be32(&p->ssr) & ~RGMII_SSR_MASK(input); | ||||
| 
 | ||||
| 	RGMII_DBG(dev, "speed(%d, %d)" NL, input, speed); | ||||
| 
 | ||||
| 	if (speed == SPEED_1000) | ||||
| 		ssr |= RGMII_SSR_1000(input); | ||||
| 	else if (speed == SPEED_100) | ||||
| 		ssr |= RGMII_SSR_100(input); | ||||
| 	else if (speed == SPEED_10) | ||||
| 		ssr |= RGMII_SSR_10(input); | ||||
| 
 | ||||
| 	out_be32(&p->ssr, ssr); | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| void rgmii_get_mdio(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct rgmii_regs __iomem *p = dev->base; | ||||
| 	u32 fer; | ||||
| 
 | ||||
| 	RGMII_DBG2(dev, "get_mdio(%d)" NL, input); | ||||
| 
 | ||||
| 	if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) | ||||
| 		return; | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	fer = in_be32(&p->fer); | ||||
| 	fer |= 0x00080000u >> input; | ||||
| 	out_be32(&p->fer, fer); | ||||
| 	(void)in_be32(&p->fer); | ||||
| 
 | ||||
| 	DBG2(dev, " fer = 0x%08x\n", fer); | ||||
| } | ||||
| 
 | ||||
| void rgmii_put_mdio(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct rgmii_regs __iomem *p = dev->base; | ||||
| 	u32 fer; | ||||
| 
 | ||||
| 	RGMII_DBG2(dev, "put_mdio(%d)" NL, input); | ||||
| 
 | ||||
| 	if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) | ||||
| 		return; | ||||
| 
 | ||||
| 	fer = in_be32(&p->fer); | ||||
| 	fer &= ~(0x00080000u >> input); | ||||
| 	out_be32(&p->fer, fer); | ||||
| 	(void)in_be32(&p->fer); | ||||
| 
 | ||||
| 	DBG2(dev, " fer = 0x%08x\n", fer); | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| void rgmii_detach(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct rgmii_regs __iomem *p; | ||||
| 
 | ||||
| 	BUG_ON(!dev || dev->users == 0); | ||||
| 	p = dev->base; | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	RGMII_DBG(dev, "detach(%d)" NL, input); | ||||
| 
 | ||||
| 	/* Disable this input */ | ||||
| 	out_be32(&p->fer, in_be32(&p->fer) & ~RGMII_FER_MASK(input)); | ||||
| 
 | ||||
| 	--dev->users; | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| int rgmii_get_regs_len(struct platform_device *ofdev) | ||||
| { | ||||
| 	return sizeof(struct emac_ethtool_regs_subhdr) + | ||||
| 		sizeof(struct rgmii_regs); | ||||
| } | ||||
| 
 | ||||
| void *rgmii_dump_regs(struct platform_device *ofdev, void *buf) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct emac_ethtool_regs_subhdr *hdr = buf; | ||||
| 	struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); | ||||
| 
 | ||||
| 	hdr->version = 0; | ||||
| 	hdr->index = 0; /* for now, are there chips with more than one
 | ||||
| 			 * rgmii ? if yes, then we'll add a cell_index | ||||
| 			 * like we do for emac | ||||
| 			 */ | ||||
| 	memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs)); | ||||
| 	return regs + 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int rgmii_probe(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct device_node *np = ofdev->dev.of_node; | ||||
| 	struct rgmii_instance *dev; | ||||
| 	struct resource regs; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL); | ||||
| 	if (dev == NULL) | ||||
| 		goto err_gone; | ||||
| 
 | ||||
| 	mutex_init(&dev->lock); | ||||
| 	dev->ofdev = ofdev; | ||||
| 
 | ||||
| 	rc = -ENXIO; | ||||
| 	if (of_address_to_resource(np, 0, ®s)) { | ||||
| 		printk(KERN_ERR "%s: Can't get registers address\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start, | ||||
| 						 sizeof(struct rgmii_regs)); | ||||
| 	if (dev->base == NULL) { | ||||
| 		printk(KERN_ERR "%s: Can't map device registers!\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check for RGMII flags */ | ||||
| 	if (of_get_property(ofdev->dev.of_node, "has-mdio", NULL)) | ||||
| 		dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; | ||||
| 
 | ||||
| 	/* CAB lacks the right properties, fix this up */ | ||||
| 	if (of_device_is_compatible(ofdev->dev.of_node, "ibm,rgmii-axon")) | ||||
| 		dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; | ||||
| 
 | ||||
| 	DBG2(dev, " Boot FER = 0x%08x, SSR = 0x%08x\n", | ||||
| 	     in_be32(&dev->base->fer), in_be32(&dev->base->ssr)); | ||||
| 
 | ||||
| 	/* Disable all inputs by default */ | ||||
| 	out_be32(&dev->base->fer, 0); | ||||
| 
 | ||||
| 	printk(KERN_INFO | ||||
| 	       "RGMII %s initialized with%s MDIO support\n", | ||||
| 	       ofdev->dev.of_node->full_name, | ||||
| 	       (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); | ||||
| 
 | ||||
| 	wmb(); | ||||
| 	platform_set_drvdata(ofdev, dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  err_free: | ||||
| 	kfree(dev); | ||||
|  err_gone: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int rgmii_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct rgmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	WARN_ON(dev->users != 0); | ||||
| 
 | ||||
| 	iounmap(dev->base); | ||||
| 	kfree(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct of_device_id rgmii_match[] = | ||||
| { | ||||
| 	{ | ||||
| 		.compatible	= "ibm,rgmii", | ||||
| 	}, | ||||
| 	{ | ||||
| 		.type		= "emac-rgmii", | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver rgmii_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "emac-rgmii", | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.of_match_table = rgmii_match, | ||||
| 	}, | ||||
| 	.probe = rgmii_probe, | ||||
| 	.remove = rgmii_remove, | ||||
| }; | ||||
| 
 | ||||
| int __init rgmii_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&rgmii_driver); | ||||
| } | ||||
| 
 | ||||
| void rgmii_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&rgmii_driver); | ||||
| } | ||||
							
								
								
									
										82
									
								
								drivers/net/ethernet/ibm/emac/rgmii.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								drivers/net/ethernet/ibm/emac/rgmii.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/rgmii.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Based on ocp_zmii.h/ibm_emac_zmii.h | ||||
|  * Armin Kuster akuster@mvista.com | ||||
|  * | ||||
|  * Copyright 2004 MontaVista Software, Inc. | ||||
|  * Matt Porter <mporter@kernel.crashing.org> | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __IBM_NEWEMAC_RGMII_H | ||||
| #define __IBM_NEWEMAC_RGMII_H | ||||
| 
 | ||||
| /* RGMII bridge type */ | ||||
| #define RGMII_STANDARD		0 | ||||
| #define RGMII_AXON		1 | ||||
| 
 | ||||
| /* RGMII bridge */ | ||||
| struct rgmii_regs { | ||||
| 	u32 fer;		/* Function enable register */ | ||||
| 	u32 ssr;		/* Speed select register */ | ||||
| }; | ||||
| 
 | ||||
| /* RGMII device */ | ||||
| struct rgmii_instance { | ||||
| 	struct rgmii_regs __iomem	*base; | ||||
| 
 | ||||
| 	/* RGMII bridge flags */ | ||||
| 	int				flags; | ||||
| #define EMAC_RGMII_FLAG_HAS_MDIO	0x00000001 | ||||
| 
 | ||||
| 	/* Only one EMAC whacks us at a time */ | ||||
| 	struct mutex			lock; | ||||
| 
 | ||||
| 	/* number of EMACs using this RGMII bridge */ | ||||
| 	int				users; | ||||
| 
 | ||||
| 	/* OF device instance */ | ||||
| 	struct platform_device		*ofdev; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_IBM_EMAC_RGMII | ||||
| 
 | ||||
| int rgmii_init(void); | ||||
| void rgmii_exit(void); | ||||
| int rgmii_attach(struct platform_device *ofdev, int input, int mode); | ||||
| void rgmii_detach(struct platform_device *ofdev, int input); | ||||
| void rgmii_get_mdio(struct platform_device *ofdev, int input); | ||||
| void rgmii_put_mdio(struct platform_device *ofdev, int input); | ||||
| void rgmii_set_speed(struct platform_device *ofdev, int input, int speed); | ||||
| int rgmii_get_regs_len(struct platform_device *ofdev); | ||||
| void *rgmii_dump_regs(struct platform_device *ofdev, void *buf); | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| # define rgmii_init()		0 | ||||
| # define rgmii_exit()		do { } while(0) | ||||
| # define rgmii_attach(x,y,z)	(-ENXIO) | ||||
| # define rgmii_detach(x,y)	do { } while(0) | ||||
| # define rgmii_get_mdio(o,i)	do { } while (0) | ||||
| # define rgmii_put_mdio(o,i)	do { } while (0) | ||||
| # define rgmii_set_speed(x,y,z)	do { } while(0) | ||||
| # define rgmii_get_regs_len(x)	0 | ||||
| # define rgmii_dump_regs(x,buf)	(buf) | ||||
| #endif				/* !CONFIG_IBM_EMAC_RGMII */ | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_RGMII_H */ | ||||
							
								
								
									
										181
									
								
								drivers/net/ethernet/ibm/emac/tah.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								drivers/net/ethernet/ibm/emac/tah.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,181 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/tah.c | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright 2004 MontaVista Software, Inc. | ||||
|  * Matt Porter <mporter@kernel.crashing.org> | ||||
|  * | ||||
|  * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net> | ||||
|  * | ||||
|  * 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/of_address.h> | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| #include "emac.h" | ||||
| #include "core.h" | ||||
| 
 | ||||
| int tah_attach(struct platform_device *ofdev, int channel) | ||||
| { | ||||
| 	struct tah_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 	/* Reset has been done at probe() time... nothing else to do for now */ | ||||
| 	++dev->users; | ||||
| 	mutex_unlock(&dev->lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void tah_detach(struct platform_device *ofdev, int channel) | ||||
| { | ||||
| 	struct tah_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 	--dev->users; | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| void tah_reset(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct tah_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct tah_regs __iomem *p = dev->base; | ||||
| 	int n; | ||||
| 
 | ||||
| 	/* Reset TAH */ | ||||
| 	out_be32(&p->mr, TAH_MR_SR); | ||||
| 	n = 100; | ||||
| 	while ((in_be32(&p->mr) & TAH_MR_SR) && n) | ||||
| 		--n; | ||||
| 
 | ||||
| 	if (unlikely(!n)) | ||||
| 		printk(KERN_ERR "%s: reset timeout\n", | ||||
| 			ofdev->dev.of_node->full_name); | ||||
| 
 | ||||
| 	/* 10KB TAH TX FIFO accommodates the max MTU of 9000 */ | ||||
| 	out_be32(&p->mr, | ||||
| 		 TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP | | ||||
| 		 TAH_MR_DIG); | ||||
| } | ||||
| 
 | ||||
| int tah_get_regs_len(struct platform_device *ofdev) | ||||
| { | ||||
| 	return sizeof(struct emac_ethtool_regs_subhdr) + | ||||
| 		sizeof(struct tah_regs); | ||||
| } | ||||
| 
 | ||||
| void *tah_dump_regs(struct platform_device *ofdev, void *buf) | ||||
| { | ||||
| 	struct tah_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct emac_ethtool_regs_subhdr *hdr = buf; | ||||
| 	struct tah_regs *regs = (struct tah_regs *)(hdr + 1); | ||||
| 
 | ||||
| 	hdr->version = 0; | ||||
| 	hdr->index = 0; /* for now, are there chips with more than one
 | ||||
| 			 * zmii ? if yes, then we'll add a cell_index | ||||
| 			 * like we do for emac | ||||
| 			 */ | ||||
| 	memcpy_fromio(regs, dev->base, sizeof(struct tah_regs)); | ||||
| 	return regs + 1; | ||||
| } | ||||
| 
 | ||||
| static int tah_probe(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct device_node *np = ofdev->dev.of_node; | ||||
| 	struct tah_instance *dev; | ||||
| 	struct resource regs; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL); | ||||
| 	if (dev == NULL) | ||||
| 		goto err_gone; | ||||
| 
 | ||||
| 	mutex_init(&dev->lock); | ||||
| 	dev->ofdev = ofdev; | ||||
| 
 | ||||
| 	rc = -ENXIO; | ||||
| 	if (of_address_to_resource(np, 0, ®s)) { | ||||
| 		printk(KERN_ERR "%s: Can't get registers address\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev->base = (struct tah_regs __iomem *)ioremap(regs.start, | ||||
| 					       sizeof(struct tah_regs)); | ||||
| 	if (dev->base == NULL) { | ||||
| 		printk(KERN_ERR "%s: Can't map device registers!\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	platform_set_drvdata(ofdev, dev); | ||||
| 
 | ||||
| 	/* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ | ||||
| 	tah_reset(ofdev); | ||||
| 
 | ||||
| 	printk(KERN_INFO | ||||
| 	       "TAH %s initialized\n", ofdev->dev.of_node->full_name); | ||||
| 	wmb(); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  err_free: | ||||
| 	kfree(dev); | ||||
|  err_gone: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int tah_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct tah_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	WARN_ON(dev->users != 0); | ||||
| 
 | ||||
| 	iounmap(dev->base); | ||||
| 	kfree(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct of_device_id tah_match[] = | ||||
| { | ||||
| 	{ | ||||
| 		.compatible	= "ibm,tah", | ||||
| 	}, | ||||
| 	/* For backward compat with old DT */ | ||||
| 	{ | ||||
| 		.type		= "tah", | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver tah_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "emac-tah", | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.of_match_table = tah_match, | ||||
| 	}, | ||||
| 	.probe = tah_probe, | ||||
| 	.remove = tah_remove, | ||||
| }; | ||||
| 
 | ||||
| int __init tah_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&tah_driver); | ||||
| } | ||||
| 
 | ||||
| void tah_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&tah_driver); | ||||
| } | ||||
							
								
								
									
										95
									
								
								drivers/net/ethernet/ibm/emac/tah.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								drivers/net/ethernet/ibm/emac/tah.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/tah.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, TAH support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright 2004 MontaVista Software, Inc. | ||||
|  * Matt Porter <mporter@kernel.crashing.org> | ||||
|  * | ||||
|  * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net> | ||||
|  * | ||||
|  * 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. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __IBM_NEWEMAC_TAH_H | ||||
| #define __IBM_NEWEMAC_TAH_H | ||||
| 
 | ||||
| /* TAH */ | ||||
| struct tah_regs { | ||||
| 	u32 revid; | ||||
| 	u32 pad[3]; | ||||
| 	u32 mr; | ||||
| 	u32 ssr0; | ||||
| 	u32 ssr1; | ||||
| 	u32 ssr2; | ||||
| 	u32 ssr3; | ||||
| 	u32 ssr4; | ||||
| 	u32 ssr5; | ||||
| 	u32 tsr; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* TAH device */ | ||||
| struct tah_instance { | ||||
| 	struct tah_regs __iomem		*base; | ||||
| 
 | ||||
| 	/* Only one EMAC whacks us at a time */ | ||||
| 	struct mutex			lock; | ||||
| 
 | ||||
| 	/* number of EMACs using this TAH */ | ||||
| 	int				users; | ||||
| 
 | ||||
| 	/* OF device instance */ | ||||
| 	struct platform_device		*ofdev; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* TAH engine */ | ||||
| #define TAH_MR_CVR		0x80000000 | ||||
| #define TAH_MR_SR		0x40000000 | ||||
| #define TAH_MR_ST_256		0x01000000 | ||||
| #define TAH_MR_ST_512		0x02000000 | ||||
| #define TAH_MR_ST_768		0x03000000 | ||||
| #define TAH_MR_ST_1024		0x04000000 | ||||
| #define TAH_MR_ST_1280		0x05000000 | ||||
| #define TAH_MR_ST_1536		0x06000000 | ||||
| #define TAH_MR_TFS_16KB		0x00000000 | ||||
| #define TAH_MR_TFS_2KB		0x00200000 | ||||
| #define TAH_MR_TFS_4KB		0x00400000 | ||||
| #define TAH_MR_TFS_6KB		0x00600000 | ||||
| #define TAH_MR_TFS_8KB		0x00800000 | ||||
| #define TAH_MR_TFS_10KB		0x00a00000 | ||||
| #define TAH_MR_DTFP		0x00100000 | ||||
| #define TAH_MR_DIG		0x00080000 | ||||
| 
 | ||||
| #ifdef CONFIG_IBM_EMAC_TAH | ||||
| 
 | ||||
| int tah_init(void); | ||||
| void tah_exit(void); | ||||
| int tah_attach(struct platform_device *ofdev, int channel); | ||||
| void tah_detach(struct platform_device *ofdev, int channel); | ||||
| void tah_reset(struct platform_device *ofdev); | ||||
| int tah_get_regs_len(struct platform_device *ofdev); | ||||
| void *tah_dump_regs(struct platform_device *ofdev, void *buf); | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| # define tah_init()		0 | ||||
| # define tah_exit()		do { } while(0) | ||||
| # define tah_attach(x,y)	(-ENXIO) | ||||
| # define tah_detach(x,y)	do { } while(0) | ||||
| # define tah_reset(x)		do { } while(0) | ||||
| # define tah_get_regs_len(x)	0 | ||||
| # define tah_dump_regs(x,buf)	(buf) | ||||
| 
 | ||||
| #endif				/* !CONFIG_IBM_EMAC_TAH */ | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_TAH_H */ | ||||
							
								
								
									
										328
									
								
								drivers/net/ethernet/ibm/emac/zmii.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								drivers/net/ethernet/ibm/emac/zmii.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,328 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/zmii.c | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  * 	Copyright 2001 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/slab.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/ethtool.h> | ||||
| #include <linux/of_address.h> | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| #include "emac.h" | ||||
| #include "core.h" | ||||
| 
 | ||||
| /* ZMIIx_FER */ | ||||
| #define ZMII_FER_MDI(idx)	(0x80000000 >> ((idx) * 4)) | ||||
| #define ZMII_FER_MDI_ALL	(ZMII_FER_MDI(0) | ZMII_FER_MDI(1) | \ | ||||
| 				 ZMII_FER_MDI(2) | ZMII_FER_MDI(3)) | ||||
| 
 | ||||
| #define ZMII_FER_SMII(idx)	(0x40000000 >> ((idx) * 4)) | ||||
| #define ZMII_FER_RMII(idx)	(0x20000000 >> ((idx) * 4)) | ||||
| #define ZMII_FER_MII(idx)	(0x10000000 >> ((idx) * 4)) | ||||
| 
 | ||||
| /* ZMIIx_SSR */ | ||||
| #define ZMII_SSR_SCI(idx)	(0x40000000 >> ((idx) * 4)) | ||||
| #define ZMII_SSR_FSS(idx)	(0x20000000 >> ((idx) * 4)) | ||||
| #define ZMII_SSR_SP(idx)	(0x10000000 >> ((idx) * 4)) | ||||
| 
 | ||||
| /* ZMII only supports MII, RMII and SMII
 | ||||
|  * we also support autodetection for backward compatibility | ||||
|  */ | ||||
| static inline int zmii_valid_mode(int mode) | ||||
| { | ||||
| 	return  mode == PHY_MODE_MII || | ||||
| 		mode == PHY_MODE_RMII || | ||||
| 		mode == PHY_MODE_SMII || | ||||
| 		mode == PHY_MODE_NA; | ||||
| } | ||||
| 
 | ||||
| static inline const char *zmii_mode_name(int mode) | ||||
| { | ||||
| 	switch (mode) { | ||||
| 	case PHY_MODE_MII: | ||||
| 		return "MII"; | ||||
| 	case PHY_MODE_RMII: | ||||
| 		return "RMII"; | ||||
| 	case PHY_MODE_SMII: | ||||
| 		return "SMII"; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u32 zmii_mode_mask(int mode, int input) | ||||
| { | ||||
| 	switch (mode) { | ||||
| 	case PHY_MODE_MII: | ||||
| 		return ZMII_FER_MII(input); | ||||
| 	case PHY_MODE_RMII: | ||||
| 		return ZMII_FER_RMII(input); | ||||
| 	case PHY_MODE_SMII: | ||||
| 		return ZMII_FER_SMII(input); | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int zmii_attach(struct platform_device *ofdev, int input, int *mode) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct zmii_regs __iomem *p = dev->base; | ||||
| 
 | ||||
| 	ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode); | ||||
| 
 | ||||
| 	if (!zmii_valid_mode(*mode)) { | ||||
| 		/* Probably an EMAC connected to RGMII,
 | ||||
| 		 * but it still may need ZMII for MDIO so | ||||
| 		 * we don't fail here. | ||||
| 		 */ | ||||
| 		dev->users++; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	/* Autodetect ZMII mode if not specified.
 | ||||
| 	 * This is only for backward compatibility with the old driver. | ||||
| 	 * Please, always specify PHY mode in your board port to avoid | ||||
| 	 * any surprises. | ||||
| 	 */ | ||||
| 	if (dev->mode == PHY_MODE_NA) { | ||||
| 		if (*mode == PHY_MODE_NA) { | ||||
| 			u32 r = dev->fer_save; | ||||
| 
 | ||||
| 			ZMII_DBG(dev, "autodetecting mode, FER = 0x%08x" NL, r); | ||||
| 
 | ||||
| 			if (r & (ZMII_FER_MII(0) | ZMII_FER_MII(1))) | ||||
| 				dev->mode = PHY_MODE_MII; | ||||
| 			else if (r & (ZMII_FER_RMII(0) | ZMII_FER_RMII(1))) | ||||
| 				dev->mode = PHY_MODE_RMII; | ||||
| 			else | ||||
| 				dev->mode = PHY_MODE_SMII; | ||||
| 		} else | ||||
| 			dev->mode = *mode; | ||||
| 
 | ||||
| 		printk(KERN_NOTICE "%s: bridge in %s mode\n", | ||||
| 		       ofdev->dev.of_node->full_name, | ||||
| 		       zmii_mode_name(dev->mode)); | ||||
| 	} else { | ||||
| 		/* All inputs must use the same mode */ | ||||
| 		if (*mode != PHY_MODE_NA && *mode != dev->mode) { | ||||
| 			printk(KERN_ERR | ||||
| 			       "%s: invalid mode %d specified for input %d\n", | ||||
| 			       ofdev->dev.of_node->full_name, *mode, input); | ||||
| 			mutex_unlock(&dev->lock); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Report back correct PHY mode,
 | ||||
| 	 * it may be used during PHY initialization. | ||||
| 	 */ | ||||
| 	*mode = dev->mode; | ||||
| 
 | ||||
| 	/* Enable this input */ | ||||
| 	out_be32(&p->fer, in_be32(&p->fer) | zmii_mode_mask(dev->mode, input)); | ||||
| 	++dev->users; | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void zmii_get_mdio(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	u32 fer; | ||||
| 
 | ||||
| 	ZMII_DBG2(dev, "get_mdio(%d)" NL, input); | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	fer = in_be32(&dev->base->fer) & ~ZMII_FER_MDI_ALL; | ||||
| 	out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input)); | ||||
| } | ||||
| 
 | ||||
| void zmii_put_mdio(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	ZMII_DBG2(dev, "put_mdio(%d)" NL, input); | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void zmii_set_speed(struct platform_device *ofdev, int input, int speed) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	u32 ssr; | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	ssr = in_be32(&dev->base->ssr); | ||||
| 
 | ||||
| 	ZMII_DBG(dev, "speed(%d, %d)" NL, input, speed); | ||||
| 
 | ||||
| 	if (speed == SPEED_100) | ||||
| 		ssr |= ZMII_SSR_SP(input); | ||||
| 	else | ||||
| 		ssr &= ~ZMII_SSR_SP(input); | ||||
| 
 | ||||
| 	out_be32(&dev->base->ssr, ssr); | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| void zmii_detach(struct platform_device *ofdev, int input) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	BUG_ON(!dev || dev->users == 0); | ||||
| 
 | ||||
| 	mutex_lock(&dev->lock); | ||||
| 
 | ||||
| 	ZMII_DBG(dev, "detach(%d)" NL, input); | ||||
| 
 | ||||
| 	/* Disable this input */ | ||||
| 	out_be32(&dev->base->fer, | ||||
| 		 in_be32(&dev->base->fer) & ~zmii_mode_mask(dev->mode, input)); | ||||
| 
 | ||||
| 	--dev->users; | ||||
| 
 | ||||
| 	mutex_unlock(&dev->lock); | ||||
| } | ||||
| 
 | ||||
| int zmii_get_regs_len(struct platform_device *ofdev) | ||||
| { | ||||
| 	return sizeof(struct emac_ethtool_regs_subhdr) + | ||||
| 		sizeof(struct zmii_regs); | ||||
| } | ||||
| 
 | ||||
| void *zmii_dump_regs(struct platform_device *ofdev, void *buf) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 	struct emac_ethtool_regs_subhdr *hdr = buf; | ||||
| 	struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1); | ||||
| 
 | ||||
| 	hdr->version = 0; | ||||
| 	hdr->index = 0; /* for now, are there chips with more than one
 | ||||
| 			 * zmii ? if yes, then we'll add a cell_index | ||||
| 			 * like we do for emac | ||||
| 			 */ | ||||
| 	memcpy_fromio(regs, dev->base, sizeof(struct zmii_regs)); | ||||
| 	return regs + 1; | ||||
| } | ||||
| 
 | ||||
| static int zmii_probe(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct device_node *np = ofdev->dev.of_node; | ||||
| 	struct zmii_instance *dev; | ||||
| 	struct resource regs; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev = kzalloc(sizeof(struct zmii_instance), GFP_KERNEL); | ||||
| 	if (dev == NULL) | ||||
| 		goto err_gone; | ||||
| 
 | ||||
| 	mutex_init(&dev->lock); | ||||
| 	dev->ofdev = ofdev; | ||||
| 	dev->mode = PHY_MODE_NA; | ||||
| 
 | ||||
| 	rc = -ENXIO; | ||||
| 	if (of_address_to_resource(np, 0, ®s)) { | ||||
| 		printk(KERN_ERR "%s: Can't get registers address\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	rc = -ENOMEM; | ||||
| 	dev->base = (struct zmii_regs __iomem *)ioremap(regs.start, | ||||
| 						sizeof(struct zmii_regs)); | ||||
| 	if (dev->base == NULL) { | ||||
| 		printk(KERN_ERR "%s: Can't map device registers!\n", | ||||
| 		       np->full_name); | ||||
| 		goto err_free; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We may need FER value for autodetection later */ | ||||
| 	dev->fer_save = in_be32(&dev->base->fer); | ||||
| 
 | ||||
| 	/* Disable all inputs by default */ | ||||
| 	out_be32(&dev->base->fer, 0); | ||||
| 
 | ||||
| 	printk(KERN_INFO | ||||
| 	       "ZMII %s initialized\n", ofdev->dev.of_node->full_name); | ||||
| 	wmb(); | ||||
| 	platform_set_drvdata(ofdev, dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  err_free: | ||||
| 	kfree(dev); | ||||
|  err_gone: | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int zmii_remove(struct platform_device *ofdev) | ||||
| { | ||||
| 	struct zmii_instance *dev = platform_get_drvdata(ofdev); | ||||
| 
 | ||||
| 	WARN_ON(dev->users != 0); | ||||
| 
 | ||||
| 	iounmap(dev->base); | ||||
| 	kfree(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct of_device_id zmii_match[] = | ||||
| { | ||||
| 	{ | ||||
| 		.compatible	= "ibm,zmii", | ||||
| 	}, | ||||
| 	/* For backward compat with old DT */ | ||||
| 	{ | ||||
| 		.type		= "emac-zmii", | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver zmii_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "emac-zmii", | ||||
| 		.owner = THIS_MODULE, | ||||
| 		.of_match_table = zmii_match, | ||||
| 	}, | ||||
| 	.probe = zmii_probe, | ||||
| 	.remove = zmii_remove, | ||||
| }; | ||||
| 
 | ||||
| int __init zmii_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&zmii_driver); | ||||
| } | ||||
| 
 | ||||
| void zmii_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&zmii_driver); | ||||
| } | ||||
							
								
								
									
										78
									
								
								drivers/net/ethernet/ibm/emac/zmii.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								drivers/net/ethernet/ibm/emac/zmii.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| /*
 | ||||
|  * drivers/net/ethernet/ibm/emac/zmii.h | ||||
|  * | ||||
|  * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support. | ||||
|  * | ||||
|  * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. | ||||
|  *                <benh@kernel.crashing.org> | ||||
|  * | ||||
|  * Based on the arch/ppc version of the driver: | ||||
|  * | ||||
|  * Copyright (c) 2004, 2005 Zultys Technologies. | ||||
|  * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||||
|  * | ||||
|  * Based on original work by | ||||
|  *      Armin Kuster <akuster@mvista.com> | ||||
|  * 	Copyright 2001 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. | ||||
|  * | ||||
|  */ | ||||
| #ifndef __IBM_NEWEMAC_ZMII_H | ||||
| #define __IBM_NEWEMAC_ZMII_H | ||||
| 
 | ||||
| /* ZMII bridge registers */ | ||||
| struct zmii_regs { | ||||
| 	u32 fer;		/* Function enable reg */ | ||||
| 	u32 ssr;		/* Speed select reg */ | ||||
| 	u32 smiirs;		/* SMII status reg */ | ||||
| }; | ||||
| 
 | ||||
| /* ZMII device */ | ||||
| struct zmii_instance { | ||||
| 	struct zmii_regs __iomem	*base; | ||||
| 
 | ||||
| 	/* Only one EMAC whacks us at a time */ | ||||
| 	struct mutex			lock; | ||||
| 
 | ||||
| 	/* subset of PHY_MODE_XXXX */ | ||||
| 	int				mode; | ||||
| 
 | ||||
| 	/* number of EMACs using this ZMII bridge */ | ||||
| 	int				users; | ||||
| 
 | ||||
| 	/* FER value left by firmware */ | ||||
| 	u32				fer_save; | ||||
| 
 | ||||
| 	/* OF device instance */ | ||||
| 	struct platform_device		*ofdev; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_IBM_EMAC_ZMII | ||||
| 
 | ||||
| int zmii_init(void); | ||||
| void zmii_exit(void); | ||||
| int zmii_attach(struct platform_device *ofdev, int input, int *mode); | ||||
| void zmii_detach(struct platform_device *ofdev, int input); | ||||
| void zmii_get_mdio(struct platform_device *ofdev, int input); | ||||
| void zmii_put_mdio(struct platform_device *ofdev, int input); | ||||
| void zmii_set_speed(struct platform_device *ofdev, int input, int speed); | ||||
| int zmii_get_regs_len(struct platform_device *ocpdev); | ||||
| void *zmii_dump_regs(struct platform_device *ofdev, void *buf); | ||||
| 
 | ||||
| #else | ||||
| # define zmii_init()		0 | ||||
| # define zmii_exit()		do { } while(0) | ||||
| # define zmii_attach(x,y,z)	(-ENXIO) | ||||
| # define zmii_detach(x,y)	do { } while(0) | ||||
| # define zmii_get_mdio(x,y)	do { } while(0) | ||||
| # define zmii_put_mdio(x,y)	do { } while(0) | ||||
| # define zmii_set_speed(x,y,z)	do { } while(0) | ||||
| # define zmii_get_regs_len(x)	0 | ||||
| # define zmii_dump_regs(x,buf)	(buf) | ||||
| #endif				/* !CONFIG_IBM_EMAC_ZMII */ | ||||
| 
 | ||||
| #endif /* __IBM_NEWEMAC_ZMII_H */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228