freescale: Move the Freescale drivers
Move the Freescale drivers into drivers/net/ethernet/freescale/ and make the necessary Kconfig and Makefile changes. CC: Sandeep Gopalpet <sandeep.kumar@freescale.com> CC: Andy Fleming <afleming@freescale.com> CC: Shlomi Gridish <gridish@freescale.com> CC: Li Yang <leoli@freescale.com> CC: Pantelis Antoniou <pantelis.antoniou@gmail.com> CC: Vitaly Bordug <vbordug@ru.mvista.com> CC: Dan Malek <dmalek@jlc.net> CC: Sylvain Munaut <tnt@246tNt.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
88
drivers/net/ethernet/freescale/Kconfig
Normal file
88
drivers/net/ethernet/freescale/Kconfig
Normal file
@@ -0,0 +1,88 @@
|
||||
#
|
||||
# Freescale device configuration
|
||||
#
|
||||
|
||||
config NET_VENDOR_FREESCALE
|
||||
bool "Freescale devices"
|
||||
depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
|
||||
M523x || M527x || M5272 || M528x || M520x || M532x || \
|
||||
IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC || \
|
||||
(PPC_MPC52xx && PPC_BESTCOMM)
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, say Y
|
||||
and read the Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
Note that the answer to this question doesn't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all
|
||||
the questions about IBM devices. If you say Y, you will be asked for
|
||||
your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_FREESCALE
|
||||
|
||||
config FEC
|
||||
bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
|
||||
depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \
|
||||
IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC)
|
||||
default IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC if ARM
|
||||
select PHYLIB
|
||||
---help---
|
||||
Say Y here if you want to use the built-in 10/100 Fast ethernet
|
||||
controller on some Motorola ColdFire and Freescale i.MX processors.
|
||||
|
||||
config FEC_MPC52xx
|
||||
tristate "FEC MPC52xx driver"
|
||||
depends on PPC_MPC52xx && PPC_BESTCOMM
|
||||
select CRC32
|
||||
select PHYLIB
|
||||
select PPC_BESTCOMM_FEC
|
||||
---help---
|
||||
This option enables support for the MPC5200's on-chip
|
||||
Fast Ethernet Controller
|
||||
If compiled as module, it will be called fec_mpc52xx.
|
||||
|
||||
config FEC_MPC52xx_MDIO
|
||||
bool "FEC MPC52xx MDIO bus driver"
|
||||
depends on FEC_MPC52xx
|
||||
default y
|
||||
---help---
|
||||
The MPC5200's FEC can connect to the Ethernet either with
|
||||
an external MII PHY chip or 10 Mbps 7-wire interface
|
||||
(Motorola? industry standard).
|
||||
If your board uses an external PHY connected to FEC, enable this.
|
||||
If not sure, enable.
|
||||
If compiled as module, it will be called fec_mpc52xx_phy.
|
||||
|
||||
source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
|
||||
|
||||
config FSL_PQ_MDIO
|
||||
tristate "Freescale PQ MDIO"
|
||||
depends on FSL_SOC
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports the MDIO bus used by the gianfar and UCC drivers.
|
||||
|
||||
config UCC_GETH
|
||||
tristate "Freescale QE Gigabit Ethernet"
|
||||
depends on QUICC_ENGINE
|
||||
select FSL_PQ_MDIO
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports the Gigabit Ethernet mode of the QUICC Engine,
|
||||
which is available on some Freescale SOCs.
|
||||
|
||||
config UGETH_TX_ON_DEMAND
|
||||
bool "Transmit on Demand support"
|
||||
depends on UCC_GETH
|
||||
|
||||
config GIANFAR
|
||||
tristate "Gianfar Ethernet"
|
||||
depends on FSL_SOC
|
||||
select FSL_PQ_MDIO
|
||||
select PHYLIB
|
||||
select CRC32
|
||||
---help---
|
||||
This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx,
|
||||
and MPC86xx family of chips, and the FEC on the 8540.
|
||||
|
||||
endif # NET_VENDOR_FREESCALE
|
18
drivers/net/ethernet/freescale/Makefile
Normal file
18
drivers/net/ethernet/freescale/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# Makefile for the Freescale network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FEC) += fec.o
|
||||
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
|
||||
ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
|
||||
obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
|
||||
endif
|
||||
obj-$(CONFIG_FS_ENET) += fs_enet/
|
||||
obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o
|
||||
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
|
||||
gianfar_driver-objs := gianfar.o \
|
||||
gianfar_ethtool.o \
|
||||
gianfar_sysfs.o
|
||||
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
|
||||
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
|
1663
drivers/net/ethernet/freescale/fec.c
Normal file
1663
drivers/net/ethernet/freescale/fec.c
Normal file
File diff suppressed because it is too large
Load Diff
148
drivers/net/ethernet/freescale/fec.h
Normal file
148
drivers/net/ethernet/freescale/fec.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* fec.h -- Fast Ethernet Controller for Motorola ColdFire SoC
|
||||
* processors.
|
||||
*
|
||||
* (C) Copyright 2000-2005, Greg Ungerer (gerg@snapgear.com)
|
||||
* (C) Copyright 2000-2001, Lineo (www.lineo.com)
|
||||
*/
|
||||
|
||||
/****************************************************************************/
|
||||
#ifndef FEC_H
|
||||
#define FEC_H
|
||||
/****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
|
||||
defined(CONFIG_M520x) || defined(CONFIG_M532x) || \
|
||||
defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
|
||||
/*
|
||||
* Just figures, Motorola would have to change the offsets for
|
||||
* registers in the same peripheral device on different models
|
||||
* of the ColdFire!
|
||||
*/
|
||||
#define FEC_IEVENT 0x004 /* Interrupt event reg */
|
||||
#define FEC_IMASK 0x008 /* Interrupt mask reg */
|
||||
#define FEC_R_DES_ACTIVE 0x010 /* Receive descriptor reg */
|
||||
#define FEC_X_DES_ACTIVE 0x014 /* Transmit descriptor reg */
|
||||
#define FEC_ECNTRL 0x024 /* Ethernet control reg */
|
||||
#define FEC_MII_DATA 0x040 /* MII manage frame reg */
|
||||
#define FEC_MII_SPEED 0x044 /* MII speed control reg */
|
||||
#define FEC_MIB_CTRLSTAT 0x064 /* MIB control/status reg */
|
||||
#define FEC_R_CNTRL 0x084 /* Receive control reg */
|
||||
#define FEC_X_CNTRL 0x0c4 /* Transmit Control reg */
|
||||
#define FEC_ADDR_LOW 0x0e4 /* Low 32bits MAC address */
|
||||
#define FEC_ADDR_HIGH 0x0e8 /* High 16bits MAC address */
|
||||
#define FEC_OPD 0x0ec /* Opcode + Pause duration */
|
||||
#define FEC_HASH_TABLE_HIGH 0x118 /* High 32bits hash table */
|
||||
#define FEC_HASH_TABLE_LOW 0x11c /* Low 32bits hash table */
|
||||
#define FEC_GRP_HASH_TABLE_HIGH 0x120 /* High 32bits hash table */
|
||||
#define FEC_GRP_HASH_TABLE_LOW 0x124 /* Low 32bits hash table */
|
||||
#define FEC_X_WMRK 0x144 /* FIFO transmit water mark */
|
||||
#define FEC_R_BOUND 0x14c /* FIFO receive bound reg */
|
||||
#define FEC_R_FSTART 0x150 /* FIFO receive start reg */
|
||||
#define FEC_R_DES_START 0x180 /* Receive descriptor ring */
|
||||
#define FEC_X_DES_START 0x184 /* Transmit descriptor ring */
|
||||
#define FEC_R_BUFF_SIZE 0x188 /* Maximum receive buff size */
|
||||
#define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */
|
||||
#define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */
|
||||
|
||||
#else
|
||||
|
||||
#define FEC_ECNTRL 0x000 /* Ethernet control reg */
|
||||
#define FEC_IEVENT 0x004 /* Interrupt even reg */
|
||||
#define FEC_IMASK 0x008 /* Interrupt mask reg */
|
||||
#define FEC_IVEC 0x00c /* Interrupt vec status reg */
|
||||
#define FEC_R_DES_ACTIVE 0x010 /* Receive descriptor reg */
|
||||
#define FEC_X_DES_ACTIVE 0x014 /* Transmit descriptor reg */
|
||||
#define FEC_MII_DATA 0x040 /* MII manage frame reg */
|
||||
#define FEC_MII_SPEED 0x044 /* MII speed control reg */
|
||||
#define FEC_R_BOUND 0x08c /* FIFO receive bound reg */
|
||||
#define FEC_R_FSTART 0x090 /* FIFO receive start reg */
|
||||
#define FEC_X_WMRK 0x0a4 /* FIFO transmit water mark */
|
||||
#define FEC_X_FSTART 0x0ac /* FIFO transmit start reg */
|
||||
#define FEC_R_CNTRL 0x104 /* Receive control reg */
|
||||
#define FEC_MAX_FRM_LEN 0x108 /* Maximum frame length reg */
|
||||
#define FEC_X_CNTRL 0x144 /* Transmit Control reg */
|
||||
#define FEC_ADDR_LOW 0x3c0 /* Low 32bits MAC address */
|
||||
#define FEC_ADDR_HIGH 0x3c4 /* High 16bits MAC address */
|
||||
#define FEC_GRP_HASH_TABLE_HIGH 0x3c8 /* High 32bits hash table */
|
||||
#define FEC_GRP_HASH_TABLE_LOW 0x3cc /* Low 32bits hash table */
|
||||
#define FEC_R_DES_START 0x3d0 /* Receive descriptor ring */
|
||||
#define FEC_X_DES_START 0x3d4 /* Transmit descriptor ring */
|
||||
#define FEC_R_BUFF_SIZE 0x3d8 /* Maximum receive buff size */
|
||||
#define FEC_FIFO_RAM 0x400 /* FIFO RAM buffer */
|
||||
|
||||
#endif /* CONFIG_M5272 */
|
||||
|
||||
|
||||
/*
|
||||
* Define the buffer descriptor structure.
|
||||
*/
|
||||
#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28)
|
||||
struct bufdesc {
|
||||
unsigned short cbd_datlen; /* Data length */
|
||||
unsigned short cbd_sc; /* Control and status info */
|
||||
unsigned long cbd_bufaddr; /* Buffer address */
|
||||
};
|
||||
#else
|
||||
struct bufdesc {
|
||||
unsigned short cbd_sc; /* Control and status info */
|
||||
unsigned short cbd_datlen; /* Data length */
|
||||
unsigned long cbd_bufaddr; /* Buffer address */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following definitions courtesy of commproc.h, which where
|
||||
* Copyright (c) 1997 Dan Malek (dmalek@jlc.net).
|
||||
*/
|
||||
#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */
|
||||
#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */
|
||||
#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */
|
||||
#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */
|
||||
#define BD_SC_CM ((ushort)0x0200) /* Continuous mode */
|
||||
#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */
|
||||
#define BD_SC_P ((ushort)0x0100) /* xmt preamble */
|
||||
#define BD_SC_BR ((ushort)0x0020) /* Break received */
|
||||
#define BD_SC_FR ((ushort)0x0010) /* Framing error */
|
||||
#define BD_SC_PR ((ushort)0x0008) /* Parity error */
|
||||
#define BD_SC_OV ((ushort)0x0002) /* Overrun */
|
||||
#define BD_SC_CD ((ushort)0x0001) /* ?? */
|
||||
|
||||
/* Buffer descriptor control/status used by Ethernet receive.
|
||||
*/
|
||||
#define BD_ENET_RX_EMPTY ((ushort)0x8000)
|
||||
#define BD_ENET_RX_WRAP ((ushort)0x2000)
|
||||
#define BD_ENET_RX_INTR ((ushort)0x1000)
|
||||
#define BD_ENET_RX_LAST ((ushort)0x0800)
|
||||
#define BD_ENET_RX_FIRST ((ushort)0x0400)
|
||||
#define BD_ENET_RX_MISS ((ushort)0x0100)
|
||||
#define BD_ENET_RX_LG ((ushort)0x0020)
|
||||
#define BD_ENET_RX_NO ((ushort)0x0010)
|
||||
#define BD_ENET_RX_SH ((ushort)0x0008)
|
||||
#define BD_ENET_RX_CR ((ushort)0x0004)
|
||||
#define BD_ENET_RX_OV ((ushort)0x0002)
|
||||
#define BD_ENET_RX_CL ((ushort)0x0001)
|
||||
#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */
|
||||
|
||||
/* Buffer descriptor control/status used by Ethernet transmit.
|
||||
*/
|
||||
#define BD_ENET_TX_READY ((ushort)0x8000)
|
||||
#define BD_ENET_TX_PAD ((ushort)0x4000)
|
||||
#define BD_ENET_TX_WRAP ((ushort)0x2000)
|
||||
#define BD_ENET_TX_INTR ((ushort)0x1000)
|
||||
#define BD_ENET_TX_LAST ((ushort)0x0800)
|
||||
#define BD_ENET_TX_TC ((ushort)0x0400)
|
||||
#define BD_ENET_TX_DEF ((ushort)0x0200)
|
||||
#define BD_ENET_TX_HB ((ushort)0x0100)
|
||||
#define BD_ENET_TX_LC ((ushort)0x0080)
|
||||
#define BD_ENET_TX_RL ((ushort)0x0040)
|
||||
#define BD_ENET_TX_RCMASK ((ushort)0x003c)
|
||||
#define BD_ENET_TX_UN ((ushort)0x0002)
|
||||
#define BD_ENET_TX_CSL ((ushort)0x0001)
|
||||
#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
#endif /* FEC_H */
|
1104
drivers/net/ethernet/freescale/fec_mpc52xx.c
Normal file
1104
drivers/net/ethernet/freescale/fec_mpc52xx.c
Normal file
File diff suppressed because it is too large
Load Diff
294
drivers/net/ethernet/freescale/fec_mpc52xx.h
Normal file
294
drivers/net/ethernet/freescale/fec_mpc52xx.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* drivers/drivers/net/fec_mpc52xx/fec.h
|
||||
*
|
||||
* Driver for the MPC5200 Fast Ethernet Controller
|
||||
*
|
||||
* Author: Dale Farnsworth <dfarnsworth@mvista.com>
|
||||
*
|
||||
* 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
|
||||
#define __DRIVERS_NET_MPC52XX_FEC_H__
|
||||
|
||||
#include <linux/phy.h>
|
||||
|
||||
/* Tunable constant */
|
||||
/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
|
||||
#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */
|
||||
#define FEC_RX_NUM_BD 256
|
||||
#define FEC_TX_NUM_BD 64
|
||||
|
||||
#define FEC_RESET_DELAY 50 /* uS */
|
||||
|
||||
#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000)
|
||||
|
||||
/* ======================================================================== */
|
||||
/* Hardware register sets & bits */
|
||||
/* ======================================================================== */
|
||||
|
||||
struct mpc52xx_fec {
|
||||
u32 fec_id; /* FEC + 0x000 */
|
||||
u32 ievent; /* FEC + 0x004 */
|
||||
u32 imask; /* FEC + 0x008 */
|
||||
|
||||
u32 reserved0[1]; /* FEC + 0x00C */
|
||||
u32 r_des_active; /* FEC + 0x010 */
|
||||
u32 x_des_active; /* FEC + 0x014 */
|
||||
u32 r_des_active_cl; /* FEC + 0x018 */
|
||||
u32 x_des_active_cl; /* FEC + 0x01C */
|
||||
u32 ivent_set; /* FEC + 0x020 */
|
||||
u32 ecntrl; /* FEC + 0x024 */
|
||||
|
||||
u32 reserved1[6]; /* FEC + 0x028-03C */
|
||||
u32 mii_data; /* FEC + 0x040 */
|
||||
u32 mii_speed; /* FEC + 0x044 */
|
||||
u32 mii_status; /* FEC + 0x048 */
|
||||
|
||||
u32 reserved2[5]; /* FEC + 0x04C-05C */
|
||||
u32 mib_data; /* FEC + 0x060 */
|
||||
u32 mib_control; /* FEC + 0x064 */
|
||||
|
||||
u32 reserved3[6]; /* FEC + 0x068-7C */
|
||||
u32 r_activate; /* FEC + 0x080 */
|
||||
u32 r_cntrl; /* FEC + 0x084 */
|
||||
u32 r_hash; /* FEC + 0x088 */
|
||||
u32 r_data; /* FEC + 0x08C */
|
||||
u32 ar_done; /* FEC + 0x090 */
|
||||
u32 r_test; /* FEC + 0x094 */
|
||||
u32 r_mib; /* FEC + 0x098 */
|
||||
u32 r_da_low; /* FEC + 0x09C */
|
||||
u32 r_da_high; /* FEC + 0x0A0 */
|
||||
|
||||
u32 reserved4[7]; /* FEC + 0x0A4-0BC */
|
||||
u32 x_activate; /* FEC + 0x0C0 */
|
||||
u32 x_cntrl; /* FEC + 0x0C4 */
|
||||
u32 backoff; /* FEC + 0x0C8 */
|
||||
u32 x_data; /* FEC + 0x0CC */
|
||||
u32 x_status; /* FEC + 0x0D0 */
|
||||
u32 x_mib; /* FEC + 0x0D4 */
|
||||
u32 x_test; /* FEC + 0x0D8 */
|
||||
u32 fdxfc_da1; /* FEC + 0x0DC */
|
||||
u32 fdxfc_da2; /* FEC + 0x0E0 */
|
||||
u32 paddr1; /* FEC + 0x0E4 */
|
||||
u32 paddr2; /* FEC + 0x0E8 */
|
||||
u32 op_pause; /* FEC + 0x0EC */
|
||||
|
||||
u32 reserved5[4]; /* FEC + 0x0F0-0FC */
|
||||
u32 instr_reg; /* FEC + 0x100 */
|
||||
u32 context_reg; /* FEC + 0x104 */
|
||||
u32 test_cntrl; /* FEC + 0x108 */
|
||||
u32 acc_reg; /* FEC + 0x10C */
|
||||
u32 ones; /* FEC + 0x110 */
|
||||
u32 zeros; /* FEC + 0x114 */
|
||||
u32 iaddr1; /* FEC + 0x118 */
|
||||
u32 iaddr2; /* FEC + 0x11C */
|
||||
u32 gaddr1; /* FEC + 0x120 */
|
||||
u32 gaddr2; /* FEC + 0x124 */
|
||||
u32 random; /* FEC + 0x128 */
|
||||
u32 rand1; /* FEC + 0x12C */
|
||||
u32 tmp; /* FEC + 0x130 */
|
||||
|
||||
u32 reserved6[3]; /* FEC + 0x134-13C */
|
||||
u32 fifo_id; /* FEC + 0x140 */
|
||||
u32 x_wmrk; /* FEC + 0x144 */
|
||||
u32 fcntrl; /* FEC + 0x148 */
|
||||
u32 r_bound; /* FEC + 0x14C */
|
||||
u32 r_fstart; /* FEC + 0x150 */
|
||||
u32 r_count; /* FEC + 0x154 */
|
||||
u32 r_lag; /* FEC + 0x158 */
|
||||
u32 r_read; /* FEC + 0x15C */
|
||||
u32 r_write; /* FEC + 0x160 */
|
||||
u32 x_count; /* FEC + 0x164 */
|
||||
u32 x_lag; /* FEC + 0x168 */
|
||||
u32 x_retry; /* FEC + 0x16C */
|
||||
u32 x_write; /* FEC + 0x170 */
|
||||
u32 x_read; /* FEC + 0x174 */
|
||||
|
||||
u32 reserved7[2]; /* FEC + 0x178-17C */
|
||||
u32 fm_cntrl; /* FEC + 0x180 */
|
||||
u32 rfifo_data; /* FEC + 0x184 */
|
||||
u32 rfifo_status; /* FEC + 0x188 */
|
||||
u32 rfifo_cntrl; /* FEC + 0x18C */
|
||||
u32 rfifo_lrf_ptr; /* FEC + 0x190 */
|
||||
u32 rfifo_lwf_ptr; /* FEC + 0x194 */
|
||||
u32 rfifo_alarm; /* FEC + 0x198 */
|
||||
u32 rfifo_rdptr; /* FEC + 0x19C */
|
||||
u32 rfifo_wrptr; /* FEC + 0x1A0 */
|
||||
u32 tfifo_data; /* FEC + 0x1A4 */
|
||||
u32 tfifo_status; /* FEC + 0x1A8 */
|
||||
u32 tfifo_cntrl; /* FEC + 0x1AC */
|
||||
u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */
|
||||
u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */
|
||||
u32 tfifo_alarm; /* FEC + 0x1B8 */
|
||||
u32 tfifo_rdptr; /* FEC + 0x1BC */
|
||||
u32 tfifo_wrptr; /* FEC + 0x1C0 */
|
||||
|
||||
u32 reset_cntrl; /* FEC + 0x1C4 */
|
||||
u32 xmit_fsm; /* FEC + 0x1C8 */
|
||||
|
||||
u32 reserved8[3]; /* FEC + 0x1CC-1D4 */
|
||||
u32 rdes_data0; /* FEC + 0x1D8 */
|
||||
u32 rdes_data1; /* FEC + 0x1DC */
|
||||
u32 r_length; /* FEC + 0x1E0 */
|
||||
u32 x_length; /* FEC + 0x1E4 */
|
||||
u32 x_addr; /* FEC + 0x1E8 */
|
||||
u32 cdes_data; /* FEC + 0x1EC */
|
||||
u32 status; /* FEC + 0x1F0 */
|
||||
u32 dma_control; /* FEC + 0x1F4 */
|
||||
u32 des_cmnd; /* FEC + 0x1F8 */
|
||||
u32 data; /* FEC + 0x1FC */
|
||||
|
||||
u32 rmon_t_drop; /* FEC + 0x200 */
|
||||
u32 rmon_t_packets; /* FEC + 0x204 */
|
||||
u32 rmon_t_bc_pkt; /* FEC + 0x208 */
|
||||
u32 rmon_t_mc_pkt; /* FEC + 0x20C */
|
||||
u32 rmon_t_crc_align; /* FEC + 0x210 */
|
||||
u32 rmon_t_undersize; /* FEC + 0x214 */
|
||||
u32 rmon_t_oversize; /* FEC + 0x218 */
|
||||
u32 rmon_t_frag; /* FEC + 0x21C */
|
||||
u32 rmon_t_jab; /* FEC + 0x220 */
|
||||
u32 rmon_t_col; /* FEC + 0x224 */
|
||||
u32 rmon_t_p64; /* FEC + 0x228 */
|
||||
u32 rmon_t_p65to127; /* FEC + 0x22C */
|
||||
u32 rmon_t_p128to255; /* FEC + 0x230 */
|
||||
u32 rmon_t_p256to511; /* FEC + 0x234 */
|
||||
u32 rmon_t_p512to1023; /* FEC + 0x238 */
|
||||
u32 rmon_t_p1024to2047; /* FEC + 0x23C */
|
||||
u32 rmon_t_p_gte2048; /* FEC + 0x240 */
|
||||
u32 rmon_t_octets; /* FEC + 0x244 */
|
||||
u32 ieee_t_drop; /* FEC + 0x248 */
|
||||
u32 ieee_t_frame_ok; /* FEC + 0x24C */
|
||||
u32 ieee_t_1col; /* FEC + 0x250 */
|
||||
u32 ieee_t_mcol; /* FEC + 0x254 */
|
||||
u32 ieee_t_def; /* FEC + 0x258 */
|
||||
u32 ieee_t_lcol; /* FEC + 0x25C */
|
||||
u32 ieee_t_excol; /* FEC + 0x260 */
|
||||
u32 ieee_t_macerr; /* FEC + 0x264 */
|
||||
u32 ieee_t_cserr; /* FEC + 0x268 */
|
||||
u32 ieee_t_sqe; /* FEC + 0x26C */
|
||||
u32 t_fdxfc; /* FEC + 0x270 */
|
||||
u32 ieee_t_octets_ok; /* FEC + 0x274 */
|
||||
|
||||
u32 reserved9[2]; /* FEC + 0x278-27C */
|
||||
u32 rmon_r_drop; /* FEC + 0x280 */
|
||||
u32 rmon_r_packets; /* FEC + 0x284 */
|
||||
u32 rmon_r_bc_pkt; /* FEC + 0x288 */
|
||||
u32 rmon_r_mc_pkt; /* FEC + 0x28C */
|
||||
u32 rmon_r_crc_align; /* FEC + 0x290 */
|
||||
u32 rmon_r_undersize; /* FEC + 0x294 */
|
||||
u32 rmon_r_oversize; /* FEC + 0x298 */
|
||||
u32 rmon_r_frag; /* FEC + 0x29C */
|
||||
u32 rmon_r_jab; /* FEC + 0x2A0 */
|
||||
|
||||
u32 rmon_r_resvd_0; /* FEC + 0x2A4 */
|
||||
|
||||
u32 rmon_r_p64; /* FEC + 0x2A8 */
|
||||
u32 rmon_r_p65to127; /* FEC + 0x2AC */
|
||||
u32 rmon_r_p128to255; /* FEC + 0x2B0 */
|
||||
u32 rmon_r_p256to511; /* FEC + 0x2B4 */
|
||||
u32 rmon_r_p512to1023; /* FEC + 0x2B8 */
|
||||
u32 rmon_r_p1024to2047; /* FEC + 0x2BC */
|
||||
u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */
|
||||
u32 rmon_r_octets; /* FEC + 0x2C4 */
|
||||
u32 ieee_r_drop; /* FEC + 0x2C8 */
|
||||
u32 ieee_r_frame_ok; /* FEC + 0x2CC */
|
||||
u32 ieee_r_crc; /* FEC + 0x2D0 */
|
||||
u32 ieee_r_align; /* FEC + 0x2D4 */
|
||||
u32 r_macerr; /* FEC + 0x2D8 */
|
||||
u32 r_fdxfc; /* FEC + 0x2DC */
|
||||
u32 ieee_r_octets_ok; /* FEC + 0x2E0 */
|
||||
|
||||
u32 reserved10[7]; /* FEC + 0x2E4-2FC */
|
||||
|
||||
u32 reserved11[64]; /* FEC + 0x300-3FF */
|
||||
};
|
||||
|
||||
#define FEC_MIB_DISABLE 0x80000000
|
||||
|
||||
#define FEC_IEVENT_HBERR 0x80000000
|
||||
#define FEC_IEVENT_BABR 0x40000000
|
||||
#define FEC_IEVENT_BABT 0x20000000
|
||||
#define FEC_IEVENT_GRA 0x10000000
|
||||
#define FEC_IEVENT_TFINT 0x08000000
|
||||
#define FEC_IEVENT_MII 0x00800000
|
||||
#define FEC_IEVENT_LATE_COL 0x00200000
|
||||
#define FEC_IEVENT_COL_RETRY_LIM 0x00100000
|
||||
#define FEC_IEVENT_XFIFO_UN 0x00080000
|
||||
#define FEC_IEVENT_XFIFO_ERROR 0x00040000
|
||||
#define FEC_IEVENT_RFIFO_ERROR 0x00020000
|
||||
|
||||
#define FEC_IMASK_HBERR 0x80000000
|
||||
#define FEC_IMASK_BABR 0x40000000
|
||||
#define FEC_IMASK_BABT 0x20000000
|
||||
#define FEC_IMASK_GRA 0x10000000
|
||||
#define FEC_IMASK_MII 0x00800000
|
||||
#define FEC_IMASK_LATE_COL 0x00200000
|
||||
#define FEC_IMASK_COL_RETRY_LIM 0x00100000
|
||||
#define FEC_IMASK_XFIFO_UN 0x00080000
|
||||
#define FEC_IMASK_XFIFO_ERROR 0x00040000
|
||||
#define FEC_IMASK_RFIFO_ERROR 0x00020000
|
||||
|
||||
/* all but MII, which is enabled separately */
|
||||
#define FEC_IMASK_ENABLE (FEC_IMASK_HBERR | FEC_IMASK_BABR | \
|
||||
FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \
|
||||
FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \
|
||||
FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR)
|
||||
|
||||
#define FEC_RCNTRL_MAX_FL_SHIFT 16
|
||||
#define FEC_RCNTRL_LOOP 0x01
|
||||
#define FEC_RCNTRL_DRT 0x02
|
||||
#define FEC_RCNTRL_MII_MODE 0x04
|
||||
#define FEC_RCNTRL_PROM 0x08
|
||||
#define FEC_RCNTRL_BC_REJ 0x10
|
||||
#define FEC_RCNTRL_FCE 0x20
|
||||
|
||||
#define FEC_TCNTRL_GTS 0x00000001
|
||||
#define FEC_TCNTRL_HBC 0x00000002
|
||||
#define FEC_TCNTRL_FDEN 0x00000004
|
||||
#define FEC_TCNTRL_TFC_PAUSE 0x00000008
|
||||
#define FEC_TCNTRL_RFC_PAUSE 0x00000010
|
||||
|
||||
#define FEC_ECNTRL_RESET 0x00000001
|
||||
#define FEC_ECNTRL_ETHER_EN 0x00000002
|
||||
|
||||
#define FEC_MII_DATA_ST 0x40000000 /* Start frame */
|
||||
#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */
|
||||
#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */
|
||||
#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */
|
||||
#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */
|
||||
#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */
|
||||
#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data mask */
|
||||
|
||||
#define FEC_MII_READ_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA)
|
||||
#define FEC_MII_WRITE_FRAME (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA)
|
||||
|
||||
#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */
|
||||
#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */
|
||||
|
||||
#define FEC_PADDR2_TYPE 0x8808
|
||||
|
||||
#define FEC_OP_PAUSE_OPCODE 0x00010000
|
||||
|
||||
#define FEC_FIFO_WMRK_256B 0x3
|
||||
|
||||
#define FEC_FIFO_STATUS_ERR 0x00400000
|
||||
#define FEC_FIFO_STATUS_UF 0x00200000
|
||||
#define FEC_FIFO_STATUS_OF 0x00100000
|
||||
|
||||
#define FEC_FIFO_CNTRL_FRAME 0x08000000
|
||||
#define FEC_FIFO_CNTRL_LTG_7 0x07000000
|
||||
|
||||
#define FEC_RESET_CNTRL_RESET_FIFO 0x02000000
|
||||
#define FEC_RESET_CNTRL_ENABLE_IS_RESET 0x01000000
|
||||
|
||||
#define FEC_XMIT_FSM_APPEND_CRC 0x02000000
|
||||
#define FEC_XMIT_FSM_ENABLE_CRC 0x01000000
|
||||
|
||||
|
||||
extern struct platform_driver mpc52xx_fec_mdio_driver;
|
||||
|
||||
#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */
|
160
drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
Normal file
160
drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
|
||||
*
|
||||
* Copyright (C) 2007 Domen Puncer, Telargo, Inc.
|
||||
* Copyright (C) 2008 Wolfram Sang, Pengutronix
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mpc52xx.h>
|
||||
#include "fec_mpc52xx.h"
|
||||
|
||||
struct mpc52xx_fec_mdio_priv {
|
||||
struct mpc52xx_fec __iomem *regs;
|
||||
int mdio_irqs[PHY_MAX_ADDR];
|
||||
};
|
||||
|
||||
static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id,
|
||||
int reg, u32 value)
|
||||
{
|
||||
struct mpc52xx_fec_mdio_priv *priv = bus->priv;
|
||||
struct mpc52xx_fec __iomem *fec = priv->regs;
|
||||
int tries = 3;
|
||||
|
||||
value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
|
||||
value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
|
||||
|
||||
out_be32(&fec->ievent, FEC_IEVENT_MII);
|
||||
out_be32(&fec->mii_data, value);
|
||||
|
||||
/* wait for it to finish, this takes about 23 us on lite5200b */
|
||||
while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
|
||||
msleep(1);
|
||||
|
||||
if (!tries)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return value & FEC_MII_DATA_OP_RD ?
|
||||
in_be32(&fec->mii_data) & FEC_MII_DATA_DATAMSK : 0;
|
||||
}
|
||||
|
||||
static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
|
||||
{
|
||||
return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, FEC_MII_READ_FRAME);
|
||||
}
|
||||
|
||||
static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg,
|
||||
u16 data)
|
||||
{
|
||||
return mpc52xx_fec_mdio_transfer(bus, phy_id, reg,
|
||||
data | FEC_MII_WRITE_FRAME);
|
||||
}
|
||||
|
||||
static int mpc52xx_fec_mdio_probe(struct platform_device *of)
|
||||
{
|
||||
struct device *dev = &of->dev;
|
||||
struct device_node *np = of->dev.of_node;
|
||||
struct mii_bus *bus;
|
||||
struct mpc52xx_fec_mdio_priv *priv;
|
||||
struct resource res;
|
||||
int err;
|
||||
|
||||
bus = mdiobus_alloc();
|
||||
if (bus == NULL)
|
||||
return -ENOMEM;
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
bus->name = "mpc52xx MII bus";
|
||||
bus->read = mpc52xx_fec_mdio_read;
|
||||
bus->write = mpc52xx_fec_mdio_write;
|
||||
|
||||
/* setup irqs */
|
||||
bus->irq = priv->mdio_irqs;
|
||||
|
||||
/* setup registers */
|
||||
err = of_address_to_resource(np, 0, &res);
|
||||
if (err)
|
||||
goto out_free;
|
||||
priv->regs = ioremap(res.start, resource_size(&res));
|
||||
if (priv->regs == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start);
|
||||
bus->priv = priv;
|
||||
|
||||
bus->parent = dev;
|
||||
dev_set_drvdata(dev, bus);
|
||||
|
||||
/* set MII speed */
|
||||
out_be32(&priv->regs->mii_speed,
|
||||
((mpc5xxx_get_bus_frequency(of->dev.of_node) >> 20) / 5) << 1);
|
||||
|
||||
err = of_mdiobus_register(bus, np);
|
||||
if (err)
|
||||
goto out_unmap;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
iounmap(priv->regs);
|
||||
out_free:
|
||||
kfree(priv);
|
||||
mdiobus_free(bus);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mpc52xx_fec_mdio_remove(struct platform_device *of)
|
||||
{
|
||||
struct device *dev = &of->dev;
|
||||
struct mii_bus *bus = dev_get_drvdata(dev);
|
||||
struct mpc52xx_fec_mdio_priv *priv = bus->priv;
|
||||
|
||||
mdiobus_unregister(bus);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
iounmap(priv->regs);
|
||||
kfree(priv);
|
||||
mdiobus_free(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mpc52xx_fec_mdio_match[] = {
|
||||
{ .compatible = "fsl,mpc5200b-mdio", },
|
||||
{ .compatible = "fsl,mpc5200-mdio", },
|
||||
{ .compatible = "mpc5200b-fec-phy", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match);
|
||||
|
||||
struct platform_driver mpc52xx_fec_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "mpc5200b-fec-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mpc52xx_fec_mdio_match,
|
||||
},
|
||||
.probe = mpc52xx_fec_mdio_probe,
|
||||
.remove = mpc52xx_fec_mdio_remove,
|
||||
};
|
||||
|
||||
/* let fec driver call it, since this has to be registered before it */
|
||||
EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
34
drivers/net/ethernet/freescale/fs_enet/Kconfig
Normal file
34
drivers/net/ethernet/freescale/fs_enet/Kconfig
Normal file
@@ -0,0 +1,34 @@
|
||||
config FS_ENET
|
||||
tristate "Freescale Ethernet Driver"
|
||||
depends on NET_VENDOR_FREESCALE && (CPM1 || CPM2 || PPC_MPC512x)
|
||||
select MII
|
||||
select PHYLIB
|
||||
|
||||
config FS_ENET_MPC5121_FEC
|
||||
def_bool y if (FS_ENET && PPC_MPC512x)
|
||||
select FS_ENET_HAS_FEC
|
||||
|
||||
config FS_ENET_HAS_SCC
|
||||
bool "Chip has an SCC usable for ethernet"
|
||||
depends on FS_ENET && (CPM1 || CPM2)
|
||||
default y
|
||||
|
||||
config FS_ENET_HAS_FCC
|
||||
bool "Chip has an FCC usable for ethernet"
|
||||
depends on FS_ENET && CPM2
|
||||
default y
|
||||
|
||||
config FS_ENET_HAS_FEC
|
||||
bool "Chip has an FEC usable for ethernet"
|
||||
depends on FS_ENET && (CPM1 || FS_ENET_MPC5121_FEC)
|
||||
select FS_ENET_MDIO_FEC
|
||||
default y
|
||||
|
||||
config FS_ENET_MDIO_FEC
|
||||
tristate "MDIO driver for FEC"
|
||||
depends on FS_ENET && (CPM1 || FS_ENET_MPC5121_FEC)
|
||||
|
||||
config FS_ENET_MDIO_FCC
|
||||
tristate "MDIO driver for FCC"
|
||||
depends on FS_ENET && CPM2
|
||||
select MDIO_BITBANG
|
14
drivers/net/ethernet/freescale/fs_enet/Makefile
Normal file
14
drivers/net/ethernet/freescale/fs_enet/Makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Makefile for the Freescale Ethernet controllers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FS_ENET) += fs_enet.o
|
||||
|
||||
fs_enet-$(CONFIG_FS_ENET_HAS_SCC) += mac-scc.o
|
||||
fs_enet-$(CONFIG_FS_ENET_HAS_FEC) += mac-fec.o
|
||||
fs_enet-$(CONFIG_FS_ENET_HAS_FCC) += mac-fcc.o
|
||||
|
||||
obj-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o
|
||||
obj-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o
|
||||
|
||||
fs_enet-objs := fs_enet-main.o $(fs_enet-m)
|
42
drivers/net/ethernet/freescale/fs_enet/fec.h
Normal file
42
drivers/net/ethernet/freescale/fs_enet/fec.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef FS_ENET_FEC_H
|
||||
#define FS_ENET_FEC_H
|
||||
|
||||
/* CRC polynomium used by the FEC for the multicast group filtering */
|
||||
#define FEC_CRC_POLY 0x04C11DB7
|
||||
|
||||
#define FEC_MAX_MULTICAST_ADDRS 64
|
||||
|
||||
/* Interrupt events/masks.
|
||||
*/
|
||||
#define FEC_ENET_HBERR 0x80000000U /* Heartbeat error */
|
||||
#define FEC_ENET_BABR 0x40000000U /* Babbling receiver */
|
||||
#define FEC_ENET_BABT 0x20000000U /* Babbling transmitter */
|
||||
#define FEC_ENET_GRA 0x10000000U /* Graceful stop complete */
|
||||
#define FEC_ENET_TXF 0x08000000U /* Full frame transmitted */
|
||||
#define FEC_ENET_TXB 0x04000000U /* A buffer was transmitted */
|
||||
#define FEC_ENET_RXF 0x02000000U /* Full frame received */
|
||||
#define FEC_ENET_RXB 0x01000000U /* A buffer was received */
|
||||
#define FEC_ENET_MII 0x00800000U /* MII interrupt */
|
||||
#define FEC_ENET_EBERR 0x00400000U /* SDMA bus error */
|
||||
|
||||
#define FEC_ECNTRL_PINMUX 0x00000004
|
||||
#define FEC_ECNTRL_ETHER_EN 0x00000002
|
||||
#define FEC_ECNTRL_RESET 0x00000001
|
||||
|
||||
#define FEC_RCNTRL_BC_REJ 0x00000010
|
||||
#define FEC_RCNTRL_PROM 0x00000008
|
||||
#define FEC_RCNTRL_MII_MODE 0x00000004
|
||||
#define FEC_RCNTRL_DRT 0x00000002
|
||||
#define FEC_RCNTRL_LOOP 0x00000001
|
||||
|
||||
#define FEC_TCNTRL_FDEN 0x00000004
|
||||
#define FEC_TCNTRL_HBC 0x00000002
|
||||
#define FEC_TCNTRL_GTS 0x00000001
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Delay to wait for FEC reset command to complete (in us)
|
||||
*/
|
||||
#define FEC_RESET_DELAY 50
|
||||
#endif
|
1196
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
Normal file
1196
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
Normal file
File diff suppressed because it is too large
Load Diff
244
drivers/net/ethernet/freescale/fs_enet/fs_enet.h
Normal file
244
drivers/net/ethernet/freescale/fs_enet/fs_enet.h
Normal file
@@ -0,0 +1,244 @@
|
||||
#ifndef FS_ENET_H
|
||||
#define FS_ENET_H
|
||||
|
||||
#include <linux/mii.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <linux/fs_enet_pd.h>
|
||||
#include <asm/fs_pd.h>
|
||||
|
||||
#ifdef CONFIG_CPM1
|
||||
#include <asm/cpm1.h>
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FS_ENET_HAS_FEC)
|
||||
#include <asm/cpm.h>
|
||||
|
||||
#if defined(CONFIG_FS_ENET_MPC5121_FEC)
|
||||
/* MPC5121 FEC has different register layout */
|
||||
struct fec {
|
||||
u32 fec_reserved0;
|
||||
u32 fec_ievent; /* Interrupt event reg */
|
||||
u32 fec_imask; /* Interrupt mask reg */
|
||||
u32 fec_reserved1;
|
||||
u32 fec_r_des_active; /* Receive descriptor reg */
|
||||
u32 fec_x_des_active; /* Transmit descriptor reg */
|
||||
u32 fec_reserved2[3];
|
||||
u32 fec_ecntrl; /* Ethernet control reg */
|
||||
u32 fec_reserved3[6];
|
||||
u32 fec_mii_data; /* MII manage frame reg */
|
||||
u32 fec_mii_speed; /* MII speed control reg */
|
||||
u32 fec_reserved4[7];
|
||||
u32 fec_mib_ctrlstat; /* MIB control/status reg */
|
||||
u32 fec_reserved5[7];
|
||||
u32 fec_r_cntrl; /* Receive control reg */
|
||||
u32 fec_reserved6[15];
|
||||
u32 fec_x_cntrl; /* Transmit Control reg */
|
||||
u32 fec_reserved7[7];
|
||||
u32 fec_addr_low; /* Low 32bits MAC address */
|
||||
u32 fec_addr_high; /* High 16bits MAC address */
|
||||
u32 fec_opd; /* Opcode + Pause duration */
|
||||
u32 fec_reserved8[10];
|
||||
u32 fec_hash_table_high; /* High 32bits hash table */
|
||||
u32 fec_hash_table_low; /* Low 32bits hash table */
|
||||
u32 fec_grp_hash_table_high; /* High 32bits hash table */
|
||||
u32 fec_grp_hash_table_low; /* Low 32bits hash table */
|
||||
u32 fec_reserved9[7];
|
||||
u32 fec_x_wmrk; /* FIFO transmit water mark */
|
||||
u32 fec_reserved10;
|
||||
u32 fec_r_bound; /* FIFO receive bound reg */
|
||||
u32 fec_r_fstart; /* FIFO receive start reg */
|
||||
u32 fec_reserved11[11];
|
||||
u32 fec_r_des_start; /* Receive descriptor ring */
|
||||
u32 fec_x_des_start; /* Transmit descriptor ring */
|
||||
u32 fec_r_buff_size; /* Maximum receive buff size */
|
||||
u32 fec_reserved12[26];
|
||||
u32 fec_dma_control; /* DMA Endian and other ctrl */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct fec_info {
|
||||
struct fec __iomem *fecp;
|
||||
u32 mii_speed;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPM2
|
||||
#include <asm/cpm2.h>
|
||||
#endif
|
||||
|
||||
/* hw driver ops */
|
||||
struct fs_ops {
|
||||
int (*setup_data)(struct net_device *dev);
|
||||
int (*allocate_bd)(struct net_device *dev);
|
||||
void (*free_bd)(struct net_device *dev);
|
||||
void (*cleanup_data)(struct net_device *dev);
|
||||
void (*set_multicast_list)(struct net_device *dev);
|
||||
void (*adjust_link)(struct net_device *dev);
|
||||
void (*restart)(struct net_device *dev);
|
||||
void (*stop)(struct net_device *dev);
|
||||
void (*napi_clear_rx_event)(struct net_device *dev);
|
||||
void (*napi_enable_rx)(struct net_device *dev);
|
||||
void (*napi_disable_rx)(struct net_device *dev);
|
||||
void (*rx_bd_done)(struct net_device *dev);
|
||||
void (*tx_kickstart)(struct net_device *dev);
|
||||
u32 (*get_int_events)(struct net_device *dev);
|
||||
void (*clear_int_events)(struct net_device *dev, u32 int_events);
|
||||
void (*ev_error)(struct net_device *dev, u32 int_events);
|
||||
int (*get_regs)(struct net_device *dev, void *p, int *sizep);
|
||||
int (*get_regs_len)(struct net_device *dev);
|
||||
void (*tx_restart)(struct net_device *dev);
|
||||
};
|
||||
|
||||
struct phy_info {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
void (*startup) (struct net_device * dev);
|
||||
void (*shutdown) (struct net_device * dev);
|
||||
void (*ack_int) (struct net_device * dev);
|
||||
};
|
||||
|
||||
/* The FEC stores dest/src/type, data, and checksum for receive packets.
|
||||
*/
|
||||
#define MAX_MTU 1508 /* Allow fullsized pppoe packets over VLAN */
|
||||
#define MIN_MTU 46 /* this is data size */
|
||||
#define CRC_LEN 4
|
||||
|
||||
#define PKT_MAXBUF_SIZE (MAX_MTU+ETH_HLEN+CRC_LEN)
|
||||
#define PKT_MINBUF_SIZE (MIN_MTU+ETH_HLEN+CRC_LEN)
|
||||
|
||||
/* Must be a multiple of 32 (to cover both FEC & FCC) */
|
||||
#define PKT_MAXBLR_SIZE ((PKT_MAXBUF_SIZE + 31) & ~31)
|
||||
/* This is needed so that invalidate_xxx wont invalidate too much */
|
||||
#define ENET_RX_ALIGN 16
|
||||
#define ENET_RX_FRSIZE L1_CACHE_ALIGN(PKT_MAXBUF_SIZE + ENET_RX_ALIGN - 1)
|
||||
|
||||
struct fs_enet_private {
|
||||
struct napi_struct napi;
|
||||
struct device *dev; /* pointer back to the device (must be initialized first) */
|
||||
struct net_device *ndev;
|
||||
spinlock_t lock; /* during all ops except TX pckt processing */
|
||||
spinlock_t tx_lock; /* during fs_start_xmit and fs_tx */
|
||||
struct fs_platform_info *fpi;
|
||||
const struct fs_ops *ops;
|
||||
int rx_ring, tx_ring;
|
||||
dma_addr_t ring_mem_addr;
|
||||
void __iomem *ring_base;
|
||||
struct sk_buff **rx_skbuff;
|
||||
struct sk_buff **tx_skbuff;
|
||||
cbd_t __iomem *rx_bd_base; /* Address of Rx and Tx buffers. */
|
||||
cbd_t __iomem *tx_bd_base;
|
||||
cbd_t __iomem *dirty_tx; /* ring entries to be free()ed. */
|
||||
cbd_t __iomem *cur_rx;
|
||||
cbd_t __iomem *cur_tx;
|
||||
int tx_free;
|
||||
struct net_device_stats stats;
|
||||
struct timer_list phy_timer_list;
|
||||
const struct phy_info *phy;
|
||||
u32 msg_enable;
|
||||
struct mii_if_info mii_if;
|
||||
unsigned int last_mii_status;
|
||||
int interrupt;
|
||||
|
||||
struct phy_device *phydev;
|
||||
int oldduplex, oldspeed, oldlink; /* current settings */
|
||||
|
||||
/* event masks */
|
||||
u32 ev_napi_rx; /* mask of NAPI rx events */
|
||||
u32 ev_rx; /* rx event mask */
|
||||
u32 ev_tx; /* tx event mask */
|
||||
u32 ev_err; /* error event mask */
|
||||
|
||||
u16 bd_rx_empty; /* mask of BD rx empty */
|
||||
u16 bd_rx_err; /* mask of BD rx errors */
|
||||
|
||||
union {
|
||||
struct {
|
||||
int idx; /* FEC1 = 0, FEC2 = 1 */
|
||||
void __iomem *fecp; /* hw registers */
|
||||
u32 hthi, htlo; /* state for multicast */
|
||||
} fec;
|
||||
|
||||
struct {
|
||||
int idx; /* FCC1-3 = 0-2 */
|
||||
void __iomem *fccp; /* hw registers */
|
||||
void __iomem *ep; /* parameter ram */
|
||||
void __iomem *fcccp; /* hw registers cont. */
|
||||
void __iomem *mem; /* FCC DPRAM */
|
||||
u32 gaddrh, gaddrl; /* group address */
|
||||
} fcc;
|
||||
|
||||
struct {
|
||||
int idx; /* FEC1 = 0, FEC2 = 1 */
|
||||
void __iomem *sccp; /* hw registers */
|
||||
void __iomem *ep; /* parameter ram */
|
||||
u32 hthi, htlo; /* state for multicast */
|
||||
} scc;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void fs_init_bds(struct net_device *dev);
|
||||
void fs_cleanup_bds(struct net_device *dev);
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#define DRV_MODULE_NAME "fs_enet"
|
||||
#define PFX DRV_MODULE_NAME ": "
|
||||
#define DRV_MODULE_VERSION "1.0"
|
||||
#define DRV_MODULE_RELDATE "Aug 8, 2005"
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
int fs_enet_platform_init(void);
|
||||
void fs_enet_platform_cleanup(void);
|
||||
|
||||
/***************************************************************************/
|
||||
/* buffer descriptor access macros */
|
||||
|
||||
/* access macros */
|
||||
#if defined(CONFIG_CPM1)
|
||||
/* for a a CPM1 __raw_xxx's are sufficient */
|
||||
#define __cbd_out32(addr, x) __raw_writel(x, addr)
|
||||
#define __cbd_out16(addr, x) __raw_writew(x, addr)
|
||||
#define __cbd_in32(addr) __raw_readl(addr)
|
||||
#define __cbd_in16(addr) __raw_readw(addr)
|
||||
#else
|
||||
/* for others play it safe */
|
||||
#define __cbd_out32(addr, x) out_be32(addr, x)
|
||||
#define __cbd_out16(addr, x) out_be16(addr, x)
|
||||
#define __cbd_in32(addr) in_be32(addr)
|
||||
#define __cbd_in16(addr) in_be16(addr)
|
||||
#endif
|
||||
|
||||
/* write */
|
||||
#define CBDW_SC(_cbd, _sc) __cbd_out16(&(_cbd)->cbd_sc, (_sc))
|
||||
#define CBDW_DATLEN(_cbd, _datlen) __cbd_out16(&(_cbd)->cbd_datlen, (_datlen))
|
||||
#define CBDW_BUFADDR(_cbd, _bufaddr) __cbd_out32(&(_cbd)->cbd_bufaddr, (_bufaddr))
|
||||
|
||||
/* read */
|
||||
#define CBDR_SC(_cbd) __cbd_in16(&(_cbd)->cbd_sc)
|
||||
#define CBDR_DATLEN(_cbd) __cbd_in16(&(_cbd)->cbd_datlen)
|
||||
#define CBDR_BUFADDR(_cbd) __cbd_in32(&(_cbd)->cbd_bufaddr)
|
||||
|
||||
/* set bits */
|
||||
#define CBDS_SC(_cbd, _sc) CBDW_SC(_cbd, CBDR_SC(_cbd) | (_sc))
|
||||
|
||||
/* clear bits */
|
||||
#define CBDC_SC(_cbd, _sc) CBDW_SC(_cbd, CBDR_SC(_cbd) & ~(_sc))
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
extern const struct fs_ops fs_fec_ops;
|
||||
extern const struct fs_ops fs_fcc_ops;
|
||||
extern const struct fs_ops fs_scc_ops;
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
#endif
|
584
drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
Normal file
584
drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
Normal file
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
* FCC driver for Motorola MPC82xx (PQ2).
|
||||
*
|
||||
* Copyright (c) 2003 Intracom S.A.
|
||||
* by Pantelis Antoniou <panto@intracom.gr>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <asm/immap_cpm2.h>
|
||||
#include <asm/mpc8260.h>
|
||||
#include <asm/cpm2.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "fs_enet.h"
|
||||
|
||||
/*************************************************/
|
||||
|
||||
/* FCC access macros */
|
||||
|
||||
/* write, read, set bits, clear bits */
|
||||
#define W32(_p, _m, _v) out_be32(&(_p)->_m, (_v))
|
||||
#define R32(_p, _m) in_be32(&(_p)->_m)
|
||||
#define S32(_p, _m, _v) W32(_p, _m, R32(_p, _m) | (_v))
|
||||
#define C32(_p, _m, _v) W32(_p, _m, R32(_p, _m) & ~(_v))
|
||||
|
||||
#define W16(_p, _m, _v) out_be16(&(_p)->_m, (_v))
|
||||
#define R16(_p, _m) in_be16(&(_p)->_m)
|
||||
#define S16(_p, _m, _v) W16(_p, _m, R16(_p, _m) | (_v))
|
||||
#define C16(_p, _m, _v) W16(_p, _m, R16(_p, _m) & ~(_v))
|
||||
|
||||
#define W8(_p, _m, _v) out_8(&(_p)->_m, (_v))
|
||||
#define R8(_p, _m) in_8(&(_p)->_m)
|
||||
#define S8(_p, _m, _v) W8(_p, _m, R8(_p, _m) | (_v))
|
||||
#define C8(_p, _m, _v) W8(_p, _m, R8(_p, _m) & ~(_v))
|
||||
|
||||
/*************************************************/
|
||||
|
||||
#define FCC_MAX_MULTICAST_ADDRS 64
|
||||
|
||||
#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
|
||||
#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
|
||||
#define mk_mii_end 0
|
||||
|
||||
#define MAX_CR_CMD_LOOPS 10000
|
||||
|
||||
static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op)
|
||||
{
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
return cpm_command(fpi->cp_command, op);
|
||||
}
|
||||
|
||||
static int do_pd_setup(struct fs_enet_private *fep)
|
||||
{
|
||||
struct platform_device *ofdev = to_platform_device(fep->dev);
|
||||
struct fs_platform_info *fpi = fep->fpi;
|
||||
int ret = -EINVAL;
|
||||
|
||||
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
|
||||
if (fep->interrupt == NO_IRQ)
|
||||
goto out;
|
||||
|
||||
fep->fcc.fccp = of_iomap(ofdev->dev.of_node, 0);
|
||||
if (!fep->fcc.fccp)
|
||||
goto out;
|
||||
|
||||
fep->fcc.ep = of_iomap(ofdev->dev.of_node, 1);
|
||||
if (!fep->fcc.ep)
|
||||
goto out_fccp;
|
||||
|
||||
fep->fcc.fcccp = of_iomap(ofdev->dev.of_node, 2);
|
||||
if (!fep->fcc.fcccp)
|
||||
goto out_ep;
|
||||
|
||||
fep->fcc.mem = (void __iomem *)cpm2_immr;
|
||||
fpi->dpram_offset = cpm_dpalloc(128, 32);
|
||||
if (IS_ERR_VALUE(fpi->dpram_offset)) {
|
||||
ret = fpi->dpram_offset;
|
||||
goto out_fcccp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_fcccp:
|
||||
iounmap(fep->fcc.fcccp);
|
||||
out_ep:
|
||||
iounmap(fep->fcc.ep);
|
||||
out_fccp:
|
||||
iounmap(fep->fcc.fccp);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define FCC_NAPI_RX_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB)
|
||||
#define FCC_RX_EVENT (FCC_ENET_RXF)
|
||||
#define FCC_TX_EVENT (FCC_ENET_TXB)
|
||||
#define FCC_ERR_EVENT_MSK (FCC_ENET_TXE)
|
||||
|
||||
static int setup_data(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (do_pd_setup(fep) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
fep->ev_napi_rx = FCC_NAPI_RX_EVENT_MSK;
|
||||
fep->ev_rx = FCC_RX_EVENT;
|
||||
fep->ev_tx = FCC_TX_EVENT;
|
||||
fep->ev_err = FCC_ERR_EVENT_MSK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
fep->ring_base = (void __iomem __force *)dma_alloc_coherent(fep->dev,
|
||||
(fpi->tx_ring + fpi->rx_ring) *
|
||||
sizeof(cbd_t), &fep->ring_mem_addr,
|
||||
GFP_KERNEL);
|
||||
if (fep->ring_base == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
if (fep->ring_base)
|
||||
dma_free_coherent(fep->dev,
|
||||
(fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t),
|
||||
(void __force *)fep->ring_base, fep->ring_mem_addr);
|
||||
}
|
||||
|
||||
static void cleanup_data(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void set_promiscuous_mode(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
S32(fccp, fcc_fpsmr, FCC_PSMR_PRO);
|
||||
}
|
||||
|
||||
static void set_multicast_start(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_enet_t __iomem *ep = fep->fcc.ep;
|
||||
|
||||
W32(ep, fen_gaddrh, 0);
|
||||
W32(ep, fen_gaddrl, 0);
|
||||
}
|
||||
|
||||
static void set_multicast_one(struct net_device *dev, const u8 *mac)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_enet_t __iomem *ep = fep->fcc.ep;
|
||||
u16 taddrh, taddrm, taddrl;
|
||||
|
||||
taddrh = ((u16)mac[5] << 8) | mac[4];
|
||||
taddrm = ((u16)mac[3] << 8) | mac[2];
|
||||
taddrl = ((u16)mac[1] << 8) | mac[0];
|
||||
|
||||
W16(ep, fen_taddrh, taddrh);
|
||||
W16(ep, fen_taddrm, taddrm);
|
||||
W16(ep, fen_taddrl, taddrl);
|
||||
fcc_cr_cmd(fep, CPM_CR_SET_GADDR);
|
||||
}
|
||||
|
||||
static void set_multicast_finish(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
fcc_enet_t __iomem *ep = fep->fcc.ep;
|
||||
|
||||
/* clear promiscuous always */
|
||||
C32(fccp, fcc_fpsmr, FCC_PSMR_PRO);
|
||||
|
||||
/* if all multi or too many multicasts; just enable all */
|
||||
if ((dev->flags & IFF_ALLMULTI) != 0 ||
|
||||
netdev_mc_count(dev) > FCC_MAX_MULTICAST_ADDRS) {
|
||||
|
||||
W32(ep, fen_gaddrh, 0xffffffff);
|
||||
W32(ep, fen_gaddrl, 0xffffffff);
|
||||
}
|
||||
|
||||
/* read back */
|
||||
fep->fcc.gaddrh = R32(ep, fen_gaddrh);
|
||||
fep->fcc.gaddrl = R32(ep, fen_gaddrl);
|
||||
}
|
||||
|
||||
static void set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
if ((dev->flags & IFF_PROMISC) == 0) {
|
||||
set_multicast_start(dev);
|
||||
netdev_for_each_mc_addr(ha, dev)
|
||||
set_multicast_one(dev, ha->addr);
|
||||
set_multicast_finish(dev);
|
||||
} else
|
||||
set_promiscuous_mode(dev);
|
||||
}
|
||||
|
||||
static void restart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
fcc_c_t __iomem *fcccp = fep->fcc.fcccp;
|
||||
fcc_enet_t __iomem *ep = fep->fcc.ep;
|
||||
dma_addr_t rx_bd_base_phys, tx_bd_base_phys;
|
||||
u16 paddrh, paddrm, paddrl;
|
||||
const unsigned char *mac;
|
||||
int i;
|
||||
|
||||
C32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT);
|
||||
|
||||
/* clear everything (slow & steady does it) */
|
||||
for (i = 0; i < sizeof(*ep); i++)
|
||||
out_8((u8 __iomem *)ep + i, 0);
|
||||
|
||||
/* get physical address */
|
||||
rx_bd_base_phys = fep->ring_mem_addr;
|
||||
tx_bd_base_phys = rx_bd_base_phys + sizeof(cbd_t) * fpi->rx_ring;
|
||||
|
||||
/* point to bds */
|
||||
W32(ep, fen_genfcc.fcc_rbase, rx_bd_base_phys);
|
||||
W32(ep, fen_genfcc.fcc_tbase, tx_bd_base_phys);
|
||||
|
||||
/* Set maximum bytes per receive buffer.
|
||||
* It must be a multiple of 32.
|
||||
*/
|
||||
W16(ep, fen_genfcc.fcc_mrblr, PKT_MAXBLR_SIZE);
|
||||
|
||||
W32(ep, fen_genfcc.fcc_rstate, (CPMFCR_GBL | CPMFCR_EB) << 24);
|
||||
W32(ep, fen_genfcc.fcc_tstate, (CPMFCR_GBL | CPMFCR_EB) << 24);
|
||||
|
||||
/* Allocate space in the reserved FCC area of DPRAM for the
|
||||
* internal buffers. No one uses this space (yet), so we
|
||||
* can do this. Later, we will add resource management for
|
||||
* this area.
|
||||
*/
|
||||
|
||||
W16(ep, fen_genfcc.fcc_riptr, fpi->dpram_offset);
|
||||
W16(ep, fen_genfcc.fcc_tiptr, fpi->dpram_offset + 32);
|
||||
|
||||
W16(ep, fen_padptr, fpi->dpram_offset + 64);
|
||||
|
||||
/* fill with special symbol... */
|
||||
memset_io(fep->fcc.mem + fpi->dpram_offset + 64, 0x88, 32);
|
||||
|
||||
W32(ep, fen_genfcc.fcc_rbptr, 0);
|
||||
W32(ep, fen_genfcc.fcc_tbptr, 0);
|
||||
W32(ep, fen_genfcc.fcc_rcrc, 0);
|
||||
W32(ep, fen_genfcc.fcc_tcrc, 0);
|
||||
W16(ep, fen_genfcc.fcc_res1, 0);
|
||||
W32(ep, fen_genfcc.fcc_res2, 0);
|
||||
|
||||
/* no CAM */
|
||||
W32(ep, fen_camptr, 0);
|
||||
|
||||
/* Set CRC preset and mask */
|
||||
W32(ep, fen_cmask, 0xdebb20e3);
|
||||
W32(ep, fen_cpres, 0xffffffff);
|
||||
|
||||
W32(ep, fen_crcec, 0); /* CRC Error counter */
|
||||
W32(ep, fen_alec, 0); /* alignment error counter */
|
||||
W32(ep, fen_disfc, 0); /* discard frame counter */
|
||||
W16(ep, fen_retlim, 15); /* Retry limit threshold */
|
||||
W16(ep, fen_pper, 0); /* Normal persistence */
|
||||
|
||||
/* set group address */
|
||||
W32(ep, fen_gaddrh, fep->fcc.gaddrh);
|
||||
W32(ep, fen_gaddrl, fep->fcc.gaddrh);
|
||||
|
||||
/* Clear hash filter tables */
|
||||
W32(ep, fen_iaddrh, 0);
|
||||
W32(ep, fen_iaddrl, 0);
|
||||
|
||||
/* Clear the Out-of-sequence TxBD */
|
||||
W16(ep, fen_tfcstat, 0);
|
||||
W16(ep, fen_tfclen, 0);
|
||||
W32(ep, fen_tfcptr, 0);
|
||||
|
||||
W16(ep, fen_mflr, PKT_MAXBUF_SIZE); /* maximum frame length register */
|
||||
W16(ep, fen_minflr, PKT_MINBUF_SIZE); /* minimum frame length register */
|
||||
|
||||
/* set address */
|
||||
mac = dev->dev_addr;
|
||||
paddrh = ((u16)mac[5] << 8) | mac[4];
|
||||
paddrm = ((u16)mac[3] << 8) | mac[2];
|
||||
paddrl = ((u16)mac[1] << 8) | mac[0];
|
||||
|
||||
W16(ep, fen_paddrh, paddrh);
|
||||
W16(ep, fen_paddrm, paddrm);
|
||||
W16(ep, fen_paddrl, paddrl);
|
||||
|
||||
W16(ep, fen_taddrh, 0);
|
||||
W16(ep, fen_taddrm, 0);
|
||||
W16(ep, fen_taddrl, 0);
|
||||
|
||||
W16(ep, fen_maxd1, 1520); /* maximum DMA1 length */
|
||||
W16(ep, fen_maxd2, 1520); /* maximum DMA2 length */
|
||||
|
||||
/* Clear stat counters, in case we ever enable RMON */
|
||||
W32(ep, fen_octc, 0);
|
||||
W32(ep, fen_colc, 0);
|
||||
W32(ep, fen_broc, 0);
|
||||
W32(ep, fen_mulc, 0);
|
||||
W32(ep, fen_uspc, 0);
|
||||
W32(ep, fen_frgc, 0);
|
||||
W32(ep, fen_ospc, 0);
|
||||
W32(ep, fen_jbrc, 0);
|
||||
W32(ep, fen_p64c, 0);
|
||||
W32(ep, fen_p65c, 0);
|
||||
W32(ep, fen_p128c, 0);
|
||||
W32(ep, fen_p256c, 0);
|
||||
W32(ep, fen_p512c, 0);
|
||||
W32(ep, fen_p1024c, 0);
|
||||
|
||||
W16(ep, fen_rfthr, 0); /* Suggested by manual */
|
||||
W16(ep, fen_rfcnt, 0);
|
||||
W16(ep, fen_cftype, 0);
|
||||
|
||||
fs_init_bds(dev);
|
||||
|
||||
/* adjust to speed (for RMII mode) */
|
||||
if (fpi->use_rmii) {
|
||||
if (fep->phydev->speed == 100)
|
||||
C8(fcccp, fcc_gfemr, 0x20);
|
||||
else
|
||||
S8(fcccp, fcc_gfemr, 0x20);
|
||||
}
|
||||
|
||||
fcc_cr_cmd(fep, CPM_CR_INIT_TRX);
|
||||
|
||||
/* clear events */
|
||||
W16(fccp, fcc_fcce, 0xffff);
|
||||
|
||||
/* Enable interrupts we wish to service */
|
||||
W16(fccp, fcc_fccm, FCC_ENET_TXE | FCC_ENET_RXF | FCC_ENET_TXB);
|
||||
|
||||
/* Set GFMR to enable Ethernet operating mode */
|
||||
W32(fccp, fcc_gfmr, FCC_GFMR_TCI | FCC_GFMR_MODE_ENET);
|
||||
|
||||
/* set sync/delimiters */
|
||||
W16(fccp, fcc_fdsr, 0xd555);
|
||||
|
||||
W32(fccp, fcc_fpsmr, FCC_PSMR_ENCRC);
|
||||
|
||||
if (fpi->use_rmii)
|
||||
S32(fccp, fcc_fpsmr, FCC_PSMR_RMII);
|
||||
|
||||
/* adjust to duplex mode */
|
||||
if (fep->phydev->duplex)
|
||||
S32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB);
|
||||
else
|
||||
C32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB);
|
||||
|
||||
/* Restore multicast and promiscuous settings */
|
||||
set_multicast_list(dev);
|
||||
|
||||
S32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT);
|
||||
}
|
||||
|
||||
static void stop(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
/* stop ethernet */
|
||||
C32(fccp, fcc_gfmr, FCC_GFMR_ENR | FCC_GFMR_ENT);
|
||||
|
||||
/* clear events */
|
||||
W16(fccp, fcc_fcce, 0xffff);
|
||||
|
||||
/* clear interrupt mask */
|
||||
W16(fccp, fcc_fccm, 0);
|
||||
|
||||
fs_cleanup_bds(dev);
|
||||
}
|
||||
|
||||
static void napi_clear_rx_event(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
W16(fccp, fcc_fcce, FCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_enable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
S16(fccp, fcc_fccm, FCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_disable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
C16(fccp, fcc_fccm, FCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void rx_bd_done(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void tx_kickstart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
S16(fccp, fcc_ftodr, 0x8000);
|
||||
}
|
||||
|
||||
static u32 get_int_events(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
return (u32)R16(fccp, fcc_fcce);
|
||||
}
|
||||
|
||||
static void clear_int_events(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
|
||||
W16(fccp, fcc_fcce, int_events & 0xffff);
|
||||
}
|
||||
|
||||
static void ev_error(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
dev_warn(fep->dev, "FS_ENET ERROR(s) 0x%x\n", int_events);
|
||||
}
|
||||
|
||||
static int get_regs(struct net_device *dev, void *p, int *sizep)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (*sizep < sizeof(fcc_t) + sizeof(fcc_enet_t) + 1)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy_fromio(p, fep->fcc.fccp, sizeof(fcc_t));
|
||||
p = (char *)p + sizeof(fcc_t);
|
||||
|
||||
memcpy_fromio(p, fep->fcc.ep, sizeof(fcc_enet_t));
|
||||
p = (char *)p + sizeof(fcc_enet_t);
|
||||
|
||||
memcpy_fromio(p, fep->fcc.fcccp, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_regs_len(struct net_device *dev)
|
||||
{
|
||||
return sizeof(fcc_t) + sizeof(fcc_enet_t) + 1;
|
||||
}
|
||||
|
||||
/* Some transmit errors cause the transmitter to shut
|
||||
* down. We now issue a restart transmit.
|
||||
* Also, to workaround 8260 device erratum CPM37, we must
|
||||
* disable and then re-enable the transmitterfollowing a
|
||||
* Late Collision, Underrun, or Retry Limit error.
|
||||
* In addition, tbptr may point beyond BDs beyond still marked
|
||||
* as ready due to internal pipelining, so we need to look back
|
||||
* through the BDs and adjust tbptr to point to the last BD
|
||||
* marked as ready. This may result in some buffers being
|
||||
* retransmitted.
|
||||
*/
|
||||
static void tx_restart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
fcc_t __iomem *fccp = fep->fcc.fccp;
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
fcc_enet_t __iomem *ep = fep->fcc.ep;
|
||||
cbd_t __iomem *curr_tbptr;
|
||||
cbd_t __iomem *recheck_bd;
|
||||
cbd_t __iomem *prev_bd;
|
||||
cbd_t __iomem *last_tx_bd;
|
||||
|
||||
last_tx_bd = fep->tx_bd_base + (fpi->tx_ring * sizeof(cbd_t));
|
||||
|
||||
/* get the current bd held in TBPTR and scan back from this point */
|
||||
recheck_bd = curr_tbptr = (cbd_t __iomem *)
|
||||
((R32(ep, fen_genfcc.fcc_tbptr) - fep->ring_mem_addr) +
|
||||
fep->ring_base);
|
||||
|
||||
prev_bd = (recheck_bd == fep->tx_bd_base) ? last_tx_bd : recheck_bd - 1;
|
||||
|
||||
/* Move through the bds in reverse, look for the earliest buffer
|
||||
* that is not ready. Adjust TBPTR to the following buffer */
|
||||
while ((CBDR_SC(prev_bd) & BD_ENET_TX_READY) != 0) {
|
||||
/* Go back one buffer */
|
||||
recheck_bd = prev_bd;
|
||||
|
||||
/* update the previous buffer */
|
||||
prev_bd = (prev_bd == fep->tx_bd_base) ? last_tx_bd : prev_bd - 1;
|
||||
|
||||
/* We should never see all bds marked as ready, check anyway */
|
||||
if (recheck_bd == curr_tbptr)
|
||||
break;
|
||||
}
|
||||
/* Now update the TBPTR and dirty flag to the current buffer */
|
||||
W32(ep, fen_genfcc.fcc_tbptr,
|
||||
(uint) (((void *)recheck_bd - fep->ring_base) +
|
||||
fep->ring_mem_addr));
|
||||
fep->dirty_tx = recheck_bd;
|
||||
|
||||
C32(fccp, fcc_gfmr, FCC_GFMR_ENT);
|
||||
udelay(10);
|
||||
S32(fccp, fcc_gfmr, FCC_GFMR_ENT);
|
||||
|
||||
fcc_cr_cmd(fep, CPM_CR_RESTART_TX);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
const struct fs_ops fs_fcc_ops = {
|
||||
.setup_data = setup_data,
|
||||
.cleanup_data = cleanup_data,
|
||||
.set_multicast_list = set_multicast_list,
|
||||
.restart = restart,
|
||||
.stop = stop,
|
||||
.napi_clear_rx_event = napi_clear_rx_event,
|
||||
.napi_enable_rx = napi_enable_rx,
|
||||
.napi_disable_rx = napi_disable_rx,
|
||||
.rx_bd_done = rx_bd_done,
|
||||
.tx_kickstart = tx_kickstart,
|
||||
.get_int_events = get_int_events,
|
||||
.clear_int_events = clear_int_events,
|
||||
.ev_error = ev_error,
|
||||
.get_regs = get_regs,
|
||||
.get_regs_len = get_regs_len,
|
||||
.tx_restart = tx_restart,
|
||||
.allocate_bd = allocate_bd,
|
||||
.free_bd = free_bd,
|
||||
};
|
498
drivers/net/ethernet/freescale/fs_enet/mac-fec.c
Normal file
498
drivers/net/ethernet/freescale/fs_enet/mac-fec.c
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Freescale Ethernet controllers
|
||||
*
|
||||
* Copyright (c) 2005 Intracom S.A.
|
||||
* by Pantelis Antoniou <panto@intracom.gr>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#ifdef CONFIG_8xx
|
||||
#include <asm/8xx_immap.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mpc8xx.h>
|
||||
#include <asm/cpm1.h>
|
||||
#endif
|
||||
|
||||
#include "fs_enet.h"
|
||||
#include "fec.h"
|
||||
|
||||
/*************************************************/
|
||||
|
||||
#if defined(CONFIG_CPM1)
|
||||
/* for a CPM1 __raw_xxx's are sufficient */
|
||||
#define __fs_out32(addr, x) __raw_writel(x, addr)
|
||||
#define __fs_out16(addr, x) __raw_writew(x, addr)
|
||||
#define __fs_in32(addr) __raw_readl(addr)
|
||||
#define __fs_in16(addr) __raw_readw(addr)
|
||||
#else
|
||||
/* for others play it safe */
|
||||
#define __fs_out32(addr, x) out_be32(addr, x)
|
||||
#define __fs_out16(addr, x) out_be16(addr, x)
|
||||
#define __fs_in32(addr) in_be32(addr)
|
||||
#define __fs_in16(addr) in_be16(addr)
|
||||
#endif
|
||||
|
||||
/* write */
|
||||
#define FW(_fecp, _reg, _v) __fs_out32(&(_fecp)->fec_ ## _reg, (_v))
|
||||
|
||||
/* read */
|
||||
#define FR(_fecp, _reg) __fs_in32(&(_fecp)->fec_ ## _reg)
|
||||
|
||||
/* set bits */
|
||||
#define FS(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) | (_v))
|
||||
|
||||
/* clear bits */
|
||||
#define FC(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) & ~(_v))
|
||||
|
||||
/*
|
||||
* Delay to wait for FEC reset command to complete (in us)
|
||||
*/
|
||||
#define FEC_RESET_DELAY 50
|
||||
|
||||
static int whack_reset(struct fec __iomem *fecp)
|
||||
{
|
||||
int i;
|
||||
|
||||
FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET);
|
||||
for (i = 0; i < FEC_RESET_DELAY; i++) {
|
||||
if ((FR(fecp, ecntrl) & FEC_ECNTRL_RESET) == 0)
|
||||
return 0; /* OK */
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int do_pd_setup(struct fs_enet_private *fep)
|
||||
{
|
||||
struct platform_device *ofdev = to_platform_device(fep->dev);
|
||||
|
||||
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
|
||||
if (fep->interrupt == NO_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
fep->fec.fecp = of_iomap(ofdev->dev.of_node, 0);
|
||||
if (!fep->fcc.fccp)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB)
|
||||
#define FEC_RX_EVENT (FEC_ENET_RXF)
|
||||
#define FEC_TX_EVENT (FEC_ENET_TXF)
|
||||
#define FEC_ERR_EVENT_MSK (FEC_ENET_HBERR | FEC_ENET_BABR | \
|
||||
FEC_ENET_BABT | FEC_ENET_EBERR)
|
||||
|
||||
static int setup_data(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (do_pd_setup(fep) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
fep->fec.hthi = 0;
|
||||
fep->fec.htlo = 0;
|
||||
|
||||
fep->ev_napi_rx = FEC_NAPI_RX_EVENT_MSK;
|
||||
fep->ev_rx = FEC_RX_EVENT;
|
||||
fep->ev_tx = FEC_TX_EVENT;
|
||||
fep->ev_err = FEC_ERR_EVENT_MSK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
fep->ring_base = (void __force __iomem *)dma_alloc_coherent(fep->dev,
|
||||
(fpi->tx_ring + fpi->rx_ring) *
|
||||
sizeof(cbd_t), &fep->ring_mem_addr,
|
||||
GFP_KERNEL);
|
||||
if (fep->ring_base == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
if(fep->ring_base)
|
||||
dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring)
|
||||
* sizeof(cbd_t),
|
||||
(void __force *)fep->ring_base,
|
||||
fep->ring_mem_addr);
|
||||
}
|
||||
|
||||
static void cleanup_data(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void set_promiscuous_mode(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FS(fecp, r_cntrl, FEC_RCNTRL_PROM);
|
||||
}
|
||||
|
||||
static void set_multicast_start(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
fep->fec.hthi = 0;
|
||||
fep->fec.htlo = 0;
|
||||
}
|
||||
|
||||
static void set_multicast_one(struct net_device *dev, const u8 *mac)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
int temp, hash_index, i, j;
|
||||
u32 crc, csrVal;
|
||||
u8 byte, msb;
|
||||
|
||||
crc = 0xffffffff;
|
||||
for (i = 0; i < 6; i++) {
|
||||
byte = mac[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
msb = crc >> 31;
|
||||
crc <<= 1;
|
||||
if (msb ^ (byte & 0x1))
|
||||
crc ^= FEC_CRC_POLY;
|
||||
byte >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
temp = (crc & 0x3f) >> 1;
|
||||
hash_index = ((temp & 0x01) << 4) |
|
||||
((temp & 0x02) << 2) |
|
||||
((temp & 0x04)) |
|
||||
((temp & 0x08) >> 2) |
|
||||
((temp & 0x10) >> 4);
|
||||
csrVal = 1 << hash_index;
|
||||
if (crc & 1)
|
||||
fep->fec.hthi |= csrVal;
|
||||
else
|
||||
fep->fec.htlo |= csrVal;
|
||||
}
|
||||
|
||||
static void set_multicast_finish(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
/* if all multi or too many multicasts; just enable all */
|
||||
if ((dev->flags & IFF_ALLMULTI) != 0 ||
|
||||
netdev_mc_count(dev) > FEC_MAX_MULTICAST_ADDRS) {
|
||||
fep->fec.hthi = 0xffffffffU;
|
||||
fep->fec.htlo = 0xffffffffU;
|
||||
}
|
||||
|
||||
FC(fecp, r_cntrl, FEC_RCNTRL_PROM);
|
||||
FW(fecp, grp_hash_table_high, fep->fec.hthi);
|
||||
FW(fecp, grp_hash_table_low, fep->fec.htlo);
|
||||
}
|
||||
|
||||
static void set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
if ((dev->flags & IFF_PROMISC) == 0) {
|
||||
set_multicast_start(dev);
|
||||
netdev_for_each_mc_addr(ha, dev)
|
||||
set_multicast_one(dev, ha->addr);
|
||||
set_multicast_finish(dev);
|
||||
} else
|
||||
set_promiscuous_mode(dev);
|
||||
}
|
||||
|
||||
static void restart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
dma_addr_t rx_bd_base_phys, tx_bd_base_phys;
|
||||
int r;
|
||||
u32 addrhi, addrlo;
|
||||
|
||||
struct mii_bus* mii = fep->phydev->bus;
|
||||
struct fec_info* fec_inf = mii->priv;
|
||||
|
||||
r = whack_reset(fep->fec.fecp);
|
||||
if (r != 0)
|
||||
dev_err(fep->dev, "FEC Reset FAILED!\n");
|
||||
/*
|
||||
* Set station address.
|
||||
*/
|
||||
addrhi = ((u32) dev->dev_addr[0] << 24) |
|
||||
((u32) dev->dev_addr[1] << 16) |
|
||||
((u32) dev->dev_addr[2] << 8) |
|
||||
(u32) dev->dev_addr[3];
|
||||
addrlo = ((u32) dev->dev_addr[4] << 24) |
|
||||
((u32) dev->dev_addr[5] << 16);
|
||||
FW(fecp, addr_low, addrhi);
|
||||
FW(fecp, addr_high, addrlo);
|
||||
|
||||
/*
|
||||
* Reset all multicast.
|
||||
*/
|
||||
FW(fecp, grp_hash_table_high, fep->fec.hthi);
|
||||
FW(fecp, grp_hash_table_low, fep->fec.htlo);
|
||||
|
||||
/*
|
||||
* Set maximum receive buffer size.
|
||||
*/
|
||||
FW(fecp, r_buff_size, PKT_MAXBLR_SIZE);
|
||||
#ifdef CONFIG_FS_ENET_MPC5121_FEC
|
||||
FW(fecp, r_cntrl, PKT_MAXBUF_SIZE << 16);
|
||||
#else
|
||||
FW(fecp, r_hash, PKT_MAXBUF_SIZE);
|
||||
#endif
|
||||
|
||||
/* get physical address */
|
||||
rx_bd_base_phys = fep->ring_mem_addr;
|
||||
tx_bd_base_phys = rx_bd_base_phys + sizeof(cbd_t) * fpi->rx_ring;
|
||||
|
||||
/*
|
||||
* Set receive and transmit descriptor base.
|
||||
*/
|
||||
FW(fecp, r_des_start, rx_bd_base_phys);
|
||||
FW(fecp, x_des_start, tx_bd_base_phys);
|
||||
|
||||
fs_init_bds(dev);
|
||||
|
||||
/*
|
||||
* Enable big endian and don't care about SDMA FC.
|
||||
*/
|
||||
#ifdef CONFIG_FS_ENET_MPC5121_FEC
|
||||
FS(fecp, dma_control, 0xC0000000);
|
||||
#else
|
||||
FW(fecp, fun_code, 0x78000000);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set MII speed.
|
||||
*/
|
||||
FW(fecp, mii_speed, fec_inf->mii_speed);
|
||||
|
||||
/*
|
||||
* Clear any outstanding interrupt.
|
||||
*/
|
||||
FW(fecp, ievent, 0xffc0);
|
||||
#ifndef CONFIG_FS_ENET_MPC5121_FEC
|
||||
FW(fecp, ivec, (virq_to_hw(fep->interrupt) / 2) << 29);
|
||||
|
||||
FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
|
||||
#else
|
||||
/*
|
||||
* Only set MII mode - do not touch maximum frame length
|
||||
* configured before.
|
||||
*/
|
||||
FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE);
|
||||
#endif
|
||||
/*
|
||||
* adjust to duplex mode
|
||||
*/
|
||||
if (fep->phydev->duplex) {
|
||||
FC(fecp, r_cntrl, FEC_RCNTRL_DRT);
|
||||
FS(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD enable */
|
||||
} else {
|
||||
FS(fecp, r_cntrl, FEC_RCNTRL_DRT);
|
||||
FC(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD disable */
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable interrupts we wish to service.
|
||||
*/
|
||||
FW(fecp, imask, FEC_ENET_TXF | FEC_ENET_TXB |
|
||||
FEC_ENET_RXF | FEC_ENET_RXB);
|
||||
|
||||
/*
|
||||
* And last, enable the transmit and receive processing.
|
||||
*/
|
||||
FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
|
||||
FW(fecp, r_des_active, 0x01000000);
|
||||
}
|
||||
|
||||
static void stop(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
struct fec_info* feci= fep->phydev->bus->priv;
|
||||
|
||||
int i;
|
||||
|
||||
if ((FR(fecp, ecntrl) & FEC_ECNTRL_ETHER_EN) == 0)
|
||||
return; /* already down */
|
||||
|
||||
FW(fecp, x_cntrl, 0x01); /* Graceful transmit stop */
|
||||
for (i = 0; ((FR(fecp, ievent) & 0x10000000) == 0) &&
|
||||
i < FEC_RESET_DELAY; i++)
|
||||
udelay(1);
|
||||
|
||||
if (i == FEC_RESET_DELAY)
|
||||
dev_warn(fep->dev, "FEC timeout on graceful transmit stop\n");
|
||||
/*
|
||||
* Disable FEC. Let only MII interrupts.
|
||||
*/
|
||||
FW(fecp, imask, 0);
|
||||
FC(fecp, ecntrl, FEC_ECNTRL_ETHER_EN);
|
||||
|
||||
fs_cleanup_bds(dev);
|
||||
|
||||
/* shut down FEC1? that's where the mii bus is */
|
||||
if (fpi->has_phy) {
|
||||
FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
|
||||
FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
|
||||
FW(fecp, ievent, FEC_ENET_MII);
|
||||
FW(fecp, mii_speed, feci->mii_speed);
|
||||
}
|
||||
}
|
||||
|
||||
static void napi_clear_rx_event(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FW(fecp, ievent, FEC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_enable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FS(fecp, imask, FEC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_disable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FC(fecp, imask, FEC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void rx_bd_done(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FW(fecp, r_des_active, 0x01000000);
|
||||
}
|
||||
|
||||
static void tx_kickstart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FW(fecp, x_des_active, 0x01000000);
|
||||
}
|
||||
|
||||
static u32 get_int_events(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
return FR(fecp, ievent) & FR(fecp, imask);
|
||||
}
|
||||
|
||||
static void clear_int_events(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
struct fec __iomem *fecp = fep->fec.fecp;
|
||||
|
||||
FW(fecp, ievent, int_events);
|
||||
}
|
||||
|
||||
static void ev_error(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
dev_warn(fep->dev, "FEC ERROR(s) 0x%x\n", int_events);
|
||||
}
|
||||
|
||||
static int get_regs(struct net_device *dev, void *p, int *sizep)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (*sizep < sizeof(struct fec))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy_fromio(p, fep->fec.fecp, sizeof(struct fec));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_regs_len(struct net_device *dev)
|
||||
{
|
||||
return sizeof(struct fec);
|
||||
}
|
||||
|
||||
static void tx_restart(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
const struct fs_ops fs_fec_ops = {
|
||||
.setup_data = setup_data,
|
||||
.cleanup_data = cleanup_data,
|
||||
.set_multicast_list = set_multicast_list,
|
||||
.restart = restart,
|
||||
.stop = stop,
|
||||
.napi_clear_rx_event = napi_clear_rx_event,
|
||||
.napi_enable_rx = napi_enable_rx,
|
||||
.napi_disable_rx = napi_disable_rx,
|
||||
.rx_bd_done = rx_bd_done,
|
||||
.tx_kickstart = tx_kickstart,
|
||||
.get_int_events = get_int_events,
|
||||
.clear_int_events = clear_int_events,
|
||||
.ev_error = ev_error,
|
||||
.get_regs = get_regs,
|
||||
.get_regs_len = get_regs_len,
|
||||
.tx_restart = tx_restart,
|
||||
.allocate_bd = allocate_bd,
|
||||
.free_bd = free_bd,
|
||||
};
|
||||
|
484
drivers/net/ethernet/freescale/fs_enet/mac-scc.c
Normal file
484
drivers/net/ethernet/freescale/fs_enet/mac-scc.c
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Ethernet on Serial Communications Controller (SCC) driver for Motorola MPC8xx and MPC82xx.
|
||||
*
|
||||
* Copyright (c) 2003 Intracom S.A.
|
||||
* by Pantelis Antoniou <panto@intracom.gr>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#ifdef CONFIG_8xx
|
||||
#include <asm/8xx_immap.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mpc8xx.h>
|
||||
#include <asm/cpm1.h>
|
||||
#endif
|
||||
|
||||
#include "fs_enet.h"
|
||||
|
||||
/*************************************************/
|
||||
#if defined(CONFIG_CPM1)
|
||||
/* for a 8xx __raw_xxx's are sufficient */
|
||||
#define __fs_out32(addr, x) __raw_writel(x, addr)
|
||||
#define __fs_out16(addr, x) __raw_writew(x, addr)
|
||||
#define __fs_out8(addr, x) __raw_writeb(x, addr)
|
||||
#define __fs_in32(addr) __raw_readl(addr)
|
||||
#define __fs_in16(addr) __raw_readw(addr)
|
||||
#define __fs_in8(addr) __raw_readb(addr)
|
||||
#else
|
||||
/* for others play it safe */
|
||||
#define __fs_out32(addr, x) out_be32(addr, x)
|
||||
#define __fs_out16(addr, x) out_be16(addr, x)
|
||||
#define __fs_in32(addr) in_be32(addr)
|
||||
#define __fs_in16(addr) in_be16(addr)
|
||||
#define __fs_out8(addr, x) out_8(addr, x)
|
||||
#define __fs_in8(addr) in_8(addr)
|
||||
#endif
|
||||
|
||||
/* write, read, set bits, clear bits */
|
||||
#define W32(_p, _m, _v) __fs_out32(&(_p)->_m, (_v))
|
||||
#define R32(_p, _m) __fs_in32(&(_p)->_m)
|
||||
#define S32(_p, _m, _v) W32(_p, _m, R32(_p, _m) | (_v))
|
||||
#define C32(_p, _m, _v) W32(_p, _m, R32(_p, _m) & ~(_v))
|
||||
|
||||
#define W16(_p, _m, _v) __fs_out16(&(_p)->_m, (_v))
|
||||
#define R16(_p, _m) __fs_in16(&(_p)->_m)
|
||||
#define S16(_p, _m, _v) W16(_p, _m, R16(_p, _m) | (_v))
|
||||
#define C16(_p, _m, _v) W16(_p, _m, R16(_p, _m) & ~(_v))
|
||||
|
||||
#define W8(_p, _m, _v) __fs_out8(&(_p)->_m, (_v))
|
||||
#define R8(_p, _m) __fs_in8(&(_p)->_m)
|
||||
#define S8(_p, _m, _v) W8(_p, _m, R8(_p, _m) | (_v))
|
||||
#define C8(_p, _m, _v) W8(_p, _m, R8(_p, _m) & ~(_v))
|
||||
|
||||
#define SCC_MAX_MULTICAST_ADDRS 64
|
||||
|
||||
/*
|
||||
* Delay to wait for SCC reset command to complete (in us)
|
||||
*/
|
||||
#define SCC_RESET_DELAY 50
|
||||
|
||||
static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op)
|
||||
{
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
return cpm_command(fpi->cp_command, op);
|
||||
}
|
||||
|
||||
static int do_pd_setup(struct fs_enet_private *fep)
|
||||
{
|
||||
struct platform_device *ofdev = to_platform_device(fep->dev);
|
||||
|
||||
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
|
||||
if (fep->interrupt == NO_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
fep->scc.sccp = of_iomap(ofdev->dev.of_node, 0);
|
||||
if (!fep->scc.sccp)
|
||||
return -EINVAL;
|
||||
|
||||
fep->scc.ep = of_iomap(ofdev->dev.of_node, 1);
|
||||
if (!fep->scc.ep) {
|
||||
iounmap(fep->scc.sccp);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SCC_NAPI_RX_EVENT_MSK (SCCE_ENET_RXF | SCCE_ENET_RXB)
|
||||
#define SCC_RX_EVENT (SCCE_ENET_RXF)
|
||||
#define SCC_TX_EVENT (SCCE_ENET_TXB)
|
||||
#define SCC_ERR_EVENT_MSK (SCCE_ENET_TXE | SCCE_ENET_BSY)
|
||||
|
||||
static int setup_data(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
do_pd_setup(fep);
|
||||
|
||||
fep->scc.hthi = 0;
|
||||
fep->scc.htlo = 0;
|
||||
|
||||
fep->ev_napi_rx = SCC_NAPI_RX_EVENT_MSK;
|
||||
fep->ev_rx = SCC_RX_EVENT;
|
||||
fep->ev_tx = SCC_TX_EVENT | SCCE_ENET_TXE;
|
||||
fep->ev_err = SCC_ERR_EVENT_MSK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
|
||||
fep->ring_mem_addr = cpm_dpalloc((fpi->tx_ring + fpi->rx_ring) *
|
||||
sizeof(cbd_t), 8);
|
||||
if (IS_ERR_VALUE(fep->ring_mem_addr))
|
||||
return -ENOMEM;
|
||||
|
||||
fep->ring_base = (void __iomem __force*)
|
||||
cpm_dpram_addr(fep->ring_mem_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_bd(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (fep->ring_base)
|
||||
cpm_dpfree(fep->ring_mem_addr);
|
||||
}
|
||||
|
||||
static void cleanup_data(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void set_promiscuous_mode(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
S16(sccp, scc_psmr, SCC_PSMR_PRO);
|
||||
}
|
||||
|
||||
static void set_multicast_start(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_enet_t __iomem *ep = fep->scc.ep;
|
||||
|
||||
W16(ep, sen_gaddr1, 0);
|
||||
W16(ep, sen_gaddr2, 0);
|
||||
W16(ep, sen_gaddr3, 0);
|
||||
W16(ep, sen_gaddr4, 0);
|
||||
}
|
||||
|
||||
static void set_multicast_one(struct net_device *dev, const u8 * mac)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_enet_t __iomem *ep = fep->scc.ep;
|
||||
u16 taddrh, taddrm, taddrl;
|
||||
|
||||
taddrh = ((u16) mac[5] << 8) | mac[4];
|
||||
taddrm = ((u16) mac[3] << 8) | mac[2];
|
||||
taddrl = ((u16) mac[1] << 8) | mac[0];
|
||||
|
||||
W16(ep, sen_taddrh, taddrh);
|
||||
W16(ep, sen_taddrm, taddrm);
|
||||
W16(ep, sen_taddrl, taddrl);
|
||||
scc_cr_cmd(fep, CPM_CR_SET_GADDR);
|
||||
}
|
||||
|
||||
static void set_multicast_finish(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
scc_enet_t __iomem *ep = fep->scc.ep;
|
||||
|
||||
/* clear promiscuous always */
|
||||
C16(sccp, scc_psmr, SCC_PSMR_PRO);
|
||||
|
||||
/* if all multi or too many multicasts; just enable all */
|
||||
if ((dev->flags & IFF_ALLMULTI) != 0 ||
|
||||
netdev_mc_count(dev) > SCC_MAX_MULTICAST_ADDRS) {
|
||||
|
||||
W16(ep, sen_gaddr1, 0xffff);
|
||||
W16(ep, sen_gaddr2, 0xffff);
|
||||
W16(ep, sen_gaddr3, 0xffff);
|
||||
W16(ep, sen_gaddr4, 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
if ((dev->flags & IFF_PROMISC) == 0) {
|
||||
set_multicast_start(dev);
|
||||
netdev_for_each_mc_addr(ha, dev)
|
||||
set_multicast_one(dev, ha->addr);
|
||||
set_multicast_finish(dev);
|
||||
} else
|
||||
set_promiscuous_mode(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called to start or restart the FEC during a link
|
||||
* change. This only happens when switching between half and full
|
||||
* duplex.
|
||||
*/
|
||||
static void restart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
scc_enet_t __iomem *ep = fep->scc.ep;
|
||||
const struct fs_platform_info *fpi = fep->fpi;
|
||||
u16 paddrh, paddrm, paddrl;
|
||||
const unsigned char *mac;
|
||||
int i;
|
||||
|
||||
C32(sccp, scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
|
||||
/* clear everything (slow & steady does it) */
|
||||
for (i = 0; i < sizeof(*ep); i++)
|
||||
__fs_out8((u8 __iomem *)ep + i, 0);
|
||||
|
||||
/* point to bds */
|
||||
W16(ep, sen_genscc.scc_rbase, fep->ring_mem_addr);
|
||||
W16(ep, sen_genscc.scc_tbase,
|
||||
fep->ring_mem_addr + sizeof(cbd_t) * fpi->rx_ring);
|
||||
|
||||
/* Initialize function code registers for big-endian.
|
||||
*/
|
||||
#ifndef CONFIG_NOT_COHERENT_CACHE
|
||||
W8(ep, sen_genscc.scc_rfcr, SCC_EB | SCC_GBL);
|
||||
W8(ep, sen_genscc.scc_tfcr, SCC_EB | SCC_GBL);
|
||||
#else
|
||||
W8(ep, sen_genscc.scc_rfcr, SCC_EB);
|
||||
W8(ep, sen_genscc.scc_tfcr, SCC_EB);
|
||||
#endif
|
||||
|
||||
/* Set maximum bytes per receive buffer.
|
||||
* This appears to be an Ethernet frame size, not the buffer
|
||||
* fragment size. It must be a multiple of four.
|
||||
*/
|
||||
W16(ep, sen_genscc.scc_mrblr, 0x5f0);
|
||||
|
||||
/* Set CRC preset and mask.
|
||||
*/
|
||||
W32(ep, sen_cpres, 0xffffffff);
|
||||
W32(ep, sen_cmask, 0xdebb20e3);
|
||||
|
||||
W32(ep, sen_crcec, 0); /* CRC Error counter */
|
||||
W32(ep, sen_alec, 0); /* alignment error counter */
|
||||
W32(ep, sen_disfc, 0); /* discard frame counter */
|
||||
|
||||
W16(ep, sen_pads, 0x8888); /* Tx short frame pad character */
|
||||
W16(ep, sen_retlim, 15); /* Retry limit threshold */
|
||||
|
||||
W16(ep, sen_maxflr, 0x5ee); /* maximum frame length register */
|
||||
|
||||
W16(ep, sen_minflr, PKT_MINBUF_SIZE); /* minimum frame length register */
|
||||
|
||||
W16(ep, sen_maxd1, 0x000005f0); /* maximum DMA1 length */
|
||||
W16(ep, sen_maxd2, 0x000005f0); /* maximum DMA2 length */
|
||||
|
||||
/* Clear hash tables.
|
||||
*/
|
||||
W16(ep, sen_gaddr1, 0);
|
||||
W16(ep, sen_gaddr2, 0);
|
||||
W16(ep, sen_gaddr3, 0);
|
||||
W16(ep, sen_gaddr4, 0);
|
||||
W16(ep, sen_iaddr1, 0);
|
||||
W16(ep, sen_iaddr2, 0);
|
||||
W16(ep, sen_iaddr3, 0);
|
||||
W16(ep, sen_iaddr4, 0);
|
||||
|
||||
/* set address
|
||||
*/
|
||||
mac = dev->dev_addr;
|
||||
paddrh = ((u16) mac[5] << 8) | mac[4];
|
||||
paddrm = ((u16) mac[3] << 8) | mac[2];
|
||||
paddrl = ((u16) mac[1] << 8) | mac[0];
|
||||
|
||||
W16(ep, sen_paddrh, paddrh);
|
||||
W16(ep, sen_paddrm, paddrm);
|
||||
W16(ep, sen_paddrl, paddrl);
|
||||
|
||||
W16(ep, sen_pper, 0);
|
||||
W16(ep, sen_taddrl, 0);
|
||||
W16(ep, sen_taddrm, 0);
|
||||
W16(ep, sen_taddrh, 0);
|
||||
|
||||
fs_init_bds(dev);
|
||||
|
||||
scc_cr_cmd(fep, CPM_CR_INIT_TRX);
|
||||
|
||||
W16(sccp, scc_scce, 0xffff);
|
||||
|
||||
/* Enable interrupts we wish to service.
|
||||
*/
|
||||
W16(sccp, scc_sccm, SCCE_ENET_TXE | SCCE_ENET_RXF | SCCE_ENET_TXB);
|
||||
|
||||
/* Set GSMR_H to enable all normal operating modes.
|
||||
* Set GSMR_L to enable Ethernet to MC68160.
|
||||
*/
|
||||
W32(sccp, scc_gsmrh, 0);
|
||||
W32(sccp, scc_gsmrl,
|
||||
SCC_GSMRL_TCI | SCC_GSMRL_TPL_48 | SCC_GSMRL_TPP_10 |
|
||||
SCC_GSMRL_MODE_ENET);
|
||||
|
||||
/* Set sync/delimiters.
|
||||
*/
|
||||
W16(sccp, scc_dsr, 0xd555);
|
||||
|
||||
/* Set processing mode. Use Ethernet CRC, catch broadcast, and
|
||||
* start frame search 22 bit times after RENA.
|
||||
*/
|
||||
W16(sccp, scc_psmr, SCC_PSMR_ENCRC | SCC_PSMR_NIB22);
|
||||
|
||||
/* Set full duplex mode if needed */
|
||||
if (fep->phydev->duplex)
|
||||
S16(sccp, scc_psmr, SCC_PSMR_LPB | SCC_PSMR_FDE);
|
||||
|
||||
S32(sccp, scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
}
|
||||
|
||||
static void stop(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
int i;
|
||||
|
||||
for (i = 0; (R16(sccp, scc_sccm) == 0) && i < SCC_RESET_DELAY; i++)
|
||||
udelay(1);
|
||||
|
||||
if (i == SCC_RESET_DELAY)
|
||||
dev_warn(fep->dev, "SCC timeout on graceful transmit stop\n");
|
||||
|
||||
W16(sccp, scc_sccm, 0);
|
||||
C32(sccp, scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
|
||||
|
||||
fs_cleanup_bds(dev);
|
||||
}
|
||||
|
||||
static void napi_clear_rx_event(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
W16(sccp, scc_scce, SCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_enable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
S16(sccp, scc_sccm, SCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void napi_disable_rx(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
C16(sccp, scc_sccm, SCC_NAPI_RX_EVENT_MSK);
|
||||
}
|
||||
|
||||
static void rx_bd_done(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void tx_kickstart(struct net_device *dev)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static u32 get_int_events(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
return (u32) R16(sccp, scc_scce);
|
||||
}
|
||||
|
||||
static void clear_int_events(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
scc_t __iomem *sccp = fep->scc.sccp;
|
||||
|
||||
W16(sccp, scc_scce, int_events & 0xffff);
|
||||
}
|
||||
|
||||
static void ev_error(struct net_device *dev, u32 int_events)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
dev_warn(fep->dev, "SCC ERROR(s) 0x%x\n", int_events);
|
||||
}
|
||||
|
||||
static int get_regs(struct net_device *dev, void *p, int *sizep)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
if (*sizep < sizeof(scc_t) + sizeof(scc_enet_t __iomem *))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy_fromio(p, fep->scc.sccp, sizeof(scc_t));
|
||||
p = (char *)p + sizeof(scc_t);
|
||||
|
||||
memcpy_fromio(p, fep->scc.ep, sizeof(scc_enet_t __iomem *));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_regs_len(struct net_device *dev)
|
||||
{
|
||||
return sizeof(scc_t) + sizeof(scc_enet_t __iomem *);
|
||||
}
|
||||
|
||||
static void tx_restart(struct net_device *dev)
|
||||
{
|
||||
struct fs_enet_private *fep = netdev_priv(dev);
|
||||
|
||||
scc_cr_cmd(fep, CPM_CR_RESTART_TX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
const struct fs_ops fs_scc_ops = {
|
||||
.setup_data = setup_data,
|
||||
.cleanup_data = cleanup_data,
|
||||
.set_multicast_list = set_multicast_list,
|
||||
.restart = restart,
|
||||
.stop = stop,
|
||||
.napi_clear_rx_event = napi_clear_rx_event,
|
||||
.napi_enable_rx = napi_enable_rx,
|
||||
.napi_disable_rx = napi_disable_rx,
|
||||
.rx_bd_done = rx_bd_done,
|
||||
.tx_kickstart = tx_kickstart,
|
||||
.get_int_events = get_int_events,
|
||||
.clear_int_events = clear_int_events,
|
||||
.ev_error = ev_error,
|
||||
.get_regs = get_regs,
|
||||
.get_regs_len = get_regs_len,
|
||||
.tx_restart = tx_restart,
|
||||
.allocate_bd = allocate_bd,
|
||||
.free_bd = free_bd,
|
||||
};
|
246
drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
Normal file
246
drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
|
||||
*
|
||||
* Copyright (c) 2003 Intracom S.A.
|
||||
* by Pantelis Antoniou <panto@intracom.gr>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mdio-bitbang.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include "fs_enet.h"
|
||||
|
||||
struct bb_info {
|
||||
struct mdiobb_ctrl ctrl;
|
||||
__be32 __iomem *dir;
|
||||
__be32 __iomem *dat;
|
||||
u32 mdio_msk;
|
||||
u32 mdc_msk;
|
||||
};
|
||||
|
||||
/* FIXME: If any other users of GPIO crop up, then these will have to
|
||||
* have some sort of global synchronization to avoid races with other
|
||||
* pins on the same port. The ideal solution would probably be to
|
||||
* bind the ports to a GPIO driver, and have this be a client of it.
|
||||
*/
|
||||
static inline void bb_set(u32 __iomem *p, u32 m)
|
||||
{
|
||||
out_be32(p, in_be32(p) | m);
|
||||
}
|
||||
|
||||
static inline void bb_clr(u32 __iomem *p, u32 m)
|
||||
{
|
||||
out_be32(p, in_be32(p) & ~m);
|
||||
}
|
||||
|
||||
static inline int bb_read(u32 __iomem *p, u32 m)
|
||||
{
|
||||
return (in_be32(p) & m) != 0;
|
||||
}
|
||||
|
||||
static inline void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
|
||||
{
|
||||
struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
|
||||
|
||||
if (dir)
|
||||
bb_set(bitbang->dir, bitbang->mdio_msk);
|
||||
else
|
||||
bb_clr(bitbang->dir, bitbang->mdio_msk);
|
||||
|
||||
/* Read back to flush the write. */
|
||||
in_be32(bitbang->dir);
|
||||
}
|
||||
|
||||
static inline int mdio_read(struct mdiobb_ctrl *ctrl)
|
||||
{
|
||||
struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
|
||||
return bb_read(bitbang->dat, bitbang->mdio_msk);
|
||||
}
|
||||
|
||||
static inline void mdio(struct mdiobb_ctrl *ctrl, int what)
|
||||
{
|
||||
struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
|
||||
|
||||
if (what)
|
||||
bb_set(bitbang->dat, bitbang->mdio_msk);
|
||||
else
|
||||
bb_clr(bitbang->dat, bitbang->mdio_msk);
|
||||
|
||||
/* Read back to flush the write. */
|
||||
in_be32(bitbang->dat);
|
||||
}
|
||||
|
||||
static inline void mdc(struct mdiobb_ctrl *ctrl, int what)
|
||||
{
|
||||
struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
|
||||
|
||||
if (what)
|
||||
bb_set(bitbang->dat, bitbang->mdc_msk);
|
||||
else
|
||||
bb_clr(bitbang->dat, bitbang->mdc_msk);
|
||||
|
||||
/* Read back to flush the write. */
|
||||
in_be32(bitbang->dat);
|
||||
}
|
||||
|
||||
static struct mdiobb_ops bb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_mdc = mdc,
|
||||
.set_mdio_dir = mdio_dir,
|
||||
.set_mdio_data = mdio,
|
||||
.get_mdio_data = mdio_read,
|
||||
};
|
||||
|
||||
static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct resource res;
|
||||
const u32 *data;
|
||||
int mdio_pin, mdc_pin, len;
|
||||
struct bb_info *bitbang = bus->priv;
|
||||
|
||||
int ret = of_address_to_resource(np, 0, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (resource_size(&res) <= 13)
|
||||
return -ENODEV;
|
||||
|
||||
/* This should really encode the pin number as well, but all
|
||||
* we get is an int, and the odds of multiple bitbang mdio buses
|
||||
* is low enough that it's not worth going too crazy.
|
||||
*/
|
||||
snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start);
|
||||
|
||||
data = of_get_property(np, "fsl,mdio-pin", &len);
|
||||
if (!data || len != 4)
|
||||
return -ENODEV;
|
||||
mdio_pin = *data;
|
||||
|
||||
data = of_get_property(np, "fsl,mdc-pin", &len);
|
||||
if (!data || len != 4)
|
||||
return -ENODEV;
|
||||
mdc_pin = *data;
|
||||
|
||||
bitbang->dir = ioremap(res.start, resource_size(&res));
|
||||
if (!bitbang->dir)
|
||||
return -ENOMEM;
|
||||
|
||||
bitbang->dat = bitbang->dir + 4;
|
||||
bitbang->mdio_msk = 1 << (31 - mdio_pin);
|
||||
bitbang->mdc_msk = 1 << (31 - mdc_pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct mii_bus *new_bus;
|
||||
struct bb_info *bitbang;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
|
||||
if (!bitbang)
|
||||
goto out;
|
||||
|
||||
bitbang->ctrl.ops = &bb_ops;
|
||||
|
||||
new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
|
||||
if (!new_bus)
|
||||
goto out_free_priv;
|
||||
|
||||
new_bus->name = "CPM2 Bitbanged MII",
|
||||
|
||||
ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node);
|
||||
if (ret)
|
||||
goto out_free_bus;
|
||||
|
||||
new_bus->phy_mask = ~0;
|
||||
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
|
||||
if (!new_bus->irq)
|
||||
goto out_unmap_regs;
|
||||
|
||||
new_bus->parent = &ofdev->dev;
|
||||
dev_set_drvdata(&ofdev->dev, new_bus);
|
||||
|
||||
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
|
||||
if (ret)
|
||||
goto out_free_irqs;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irqs:
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
kfree(new_bus->irq);
|
||||
out_unmap_regs:
|
||||
iounmap(bitbang->dir);
|
||||
out_free_bus:
|
||||
free_mdio_bitbang(new_bus);
|
||||
out_free_priv:
|
||||
kfree(bitbang);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fs_enet_mdio_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
|
||||
struct bb_info *bitbang = bus->priv;
|
||||
|
||||
mdiobus_unregister(bus);
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
kfree(bus->irq);
|
||||
free_mdio_bitbang(bus);
|
||||
iounmap(bitbang->dir);
|
||||
kfree(bitbang);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id fs_enet_mdio_bb_match[] = {
|
||||
{
|
||||
.compatible = "fsl,cpm2-mdio-bitbang",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match);
|
||||
|
||||
static struct platform_driver fs_enet_bb_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-bb-mdio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fs_enet_mdio_bb_match,
|
||||
},
|
||||
.probe = fs_enet_mdio_probe,
|
||||
.remove = fs_enet_mdio_remove,
|
||||
};
|
||||
|
||||
static int fs_enet_mdio_bb_init(void)
|
||||
{
|
||||
return platform_driver_register(&fs_enet_bb_mdio_driver);
|
||||
}
|
||||
|
||||
static void fs_enet_mdio_bb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fs_enet_bb_mdio_driver);
|
||||
}
|
||||
|
||||
module_init(fs_enet_mdio_bb_init);
|
||||
module_exit(fs_enet_mdio_bb_exit);
|
251
drivers/net/ethernet/freescale/fs_enet/mii-fec.c
Normal file
251
drivers/net/ethernet/freescale/fs_enet/mii-fec.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
|
||||
*
|
||||
* Copyright (c) 2003 Intracom S.A.
|
||||
* by Pantelis Antoniou <panto@intracom.gr>
|
||||
*
|
||||
* 2005 (c) MontaVista Software, Inc.
|
||||
* Vitaly Bordug <vbordug@ru.mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mpc5xxx.h>
|
||||
|
||||
#include "fs_enet.h"
|
||||
#include "fec.h"
|
||||
|
||||
/* Make MII read/write commands for the FEC.
|
||||
*/
|
||||
#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
|
||||
#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
|
||||
#define mk_mii_end 0
|
||||
|
||||
#define FEC_MII_LOOPS 10000
|
||||
|
||||
static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location)
|
||||
{
|
||||
struct fec_info* fec = bus->priv;
|
||||
struct fec __iomem *fecp = fec->fecp;
|
||||
int i, ret = -1;
|
||||
|
||||
BUG_ON((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0);
|
||||
|
||||
/* Add PHY address to register command. */
|
||||
out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_read(location));
|
||||
|
||||
for (i = 0; i < FEC_MII_LOOPS; i++)
|
||||
if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
|
||||
break;
|
||||
|
||||
if (i < FEC_MII_LOOPS) {
|
||||
out_be32(&fecp->fec_ievent, FEC_ENET_MII);
|
||||
ret = in_be32(&fecp->fec_mii_data) & 0xffff;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fs_enet_fec_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val)
|
||||
{
|
||||
struct fec_info* fec = bus->priv;
|
||||
struct fec __iomem *fecp = fec->fecp;
|
||||
int i;
|
||||
|
||||
/* this must never happen */
|
||||
BUG_ON((in_be32(&fecp->fec_r_cntrl) & FEC_RCNTRL_MII_MODE) == 0);
|
||||
|
||||
/* Add PHY address to register command. */
|
||||
out_be32(&fecp->fec_mii_data, (phy_id << 23) | mk_mii_write(location, val));
|
||||
|
||||
for (i = 0; i < FEC_MII_LOOPS; i++)
|
||||
if ((in_be32(&fecp->fec_ievent) & FEC_ENET_MII) != 0)
|
||||
break;
|
||||
|
||||
if (i < FEC_MII_LOOPS)
|
||||
out_be32(&fecp->fec_ievent, FEC_ENET_MII);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int fs_enet_fec_mii_reset(struct mii_bus *bus)
|
||||
{
|
||||
/* nothing here - for now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id fs_enet_mdio_fec_match[];
|
||||
static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct resource res;
|
||||
struct mii_bus *new_bus;
|
||||
struct fec_info *fec;
|
||||
int (*get_bus_freq)(struct device_node *);
|
||||
int ret = -ENOMEM, clock, speed;
|
||||
|
||||
match = of_match_device(fs_enet_mdio_fec_match, &ofdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
get_bus_freq = match->data;
|
||||
|
||||
new_bus = mdiobus_alloc();
|
||||
if (!new_bus)
|
||||
goto out;
|
||||
|
||||
fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL);
|
||||
if (!fec)
|
||||
goto out_mii;
|
||||
|
||||
new_bus->priv = fec;
|
||||
new_bus->name = "FEC MII Bus";
|
||||
new_bus->read = &fs_enet_fec_mii_read;
|
||||
new_bus->write = &fs_enet_fec_mii_write;
|
||||
new_bus->reset = &fs_enet_fec_mii_reset;
|
||||
|
||||
ret = of_address_to_resource(ofdev->dev.of_node, 0, &res);
|
||||
if (ret)
|
||||
goto out_res;
|
||||
|
||||
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start);
|
||||
|
||||
fec->fecp = ioremap(res.start, resource_size(&res));
|
||||
if (!fec->fecp)
|
||||
goto out_fec;
|
||||
|
||||
if (get_bus_freq) {
|
||||
clock = get_bus_freq(ofdev->dev.of_node);
|
||||
if (!clock) {
|
||||
/* Use maximum divider if clock is unknown */
|
||||
dev_warn(&ofdev->dev, "could not determine IPS clock\n");
|
||||
clock = 0x3F * 5000000;
|
||||
}
|
||||
} else
|
||||
clock = ppc_proc_freq;
|
||||
|
||||
/*
|
||||
* Scale for a MII clock <= 2.5 MHz
|
||||
* Note that only 6 bits (25:30) are available for MII speed.
|
||||
*/
|
||||
speed = (clock + 4999999) / 5000000;
|
||||
if (speed > 0x3F) {
|
||||
speed = 0x3F;
|
||||
dev_err(&ofdev->dev,
|
||||
"MII clock (%d Hz) exceeds max (2.5 MHz)\n",
|
||||
clock / speed);
|
||||
}
|
||||
|
||||
fec->mii_speed = speed << 1;
|
||||
|
||||
setbits32(&fec->fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE);
|
||||
setbits32(&fec->fecp->fec_ecntrl, FEC_ECNTRL_PINMUX |
|
||||
FEC_ECNTRL_ETHER_EN);
|
||||
out_be32(&fec->fecp->fec_ievent, FEC_ENET_MII);
|
||||
clrsetbits_be32(&fec->fecp->fec_mii_speed, 0x7E, fec->mii_speed);
|
||||
|
||||
new_bus->phy_mask = ~0;
|
||||
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
|
||||
if (!new_bus->irq)
|
||||
goto out_unmap_regs;
|
||||
|
||||
new_bus->parent = &ofdev->dev;
|
||||
dev_set_drvdata(&ofdev->dev, new_bus);
|
||||
|
||||
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
|
||||
if (ret)
|
||||
goto out_free_irqs;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irqs:
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
kfree(new_bus->irq);
|
||||
out_unmap_regs:
|
||||
iounmap(fec->fecp);
|
||||
out_res:
|
||||
out_fec:
|
||||
kfree(fec);
|
||||
out_mii:
|
||||
mdiobus_free(new_bus);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fs_enet_mdio_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
|
||||
struct fec_info *fec = bus->priv;
|
||||
|
||||
mdiobus_unregister(bus);
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
kfree(bus->irq);
|
||||
iounmap(fec->fecp);
|
||||
kfree(fec);
|
||||
mdiobus_free(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id fs_enet_mdio_fec_match[] = {
|
||||
{
|
||||
.compatible = "fsl,pq1-fec-mdio",
|
||||
},
|
||||
#if defined(CONFIG_PPC_MPC512x)
|
||||
{
|
||||
.compatible = "fsl,mpc5121-fec-mdio",
|
||||
.data = mpc5xxx_get_bus_frequency,
|
||||
},
|
||||
#endif
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fs_enet_mdio_fec_match);
|
||||
|
||||
static struct platform_driver fs_enet_fec_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-fec-mdio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fs_enet_mdio_fec_match,
|
||||
},
|
||||
.probe = fs_enet_mdio_probe,
|
||||
.remove = fs_enet_mdio_remove,
|
||||
};
|
||||
|
||||
static int fs_enet_mdio_fec_init(void)
|
||||
{
|
||||
return platform_driver_register(&fs_enet_fec_mdio_driver);
|
||||
}
|
||||
|
||||
static void fs_enet_mdio_fec_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fs_enet_fec_mdio_driver);
|
||||
}
|
||||
|
||||
module_init(fs_enet_mdio_fec_init);
|
||||
module_exit(fs_enet_mdio_fec_exit);
|
494
drivers/net/ethernet/freescale/fsl_pq_mdio.c
Normal file
494
drivers/net/ethernet/freescale/fsl_pq_mdio.c
Normal file
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Freescale PowerQUICC Ethernet Driver -- MIIM bus implementation
|
||||
* Provides Bus interface for MIIM regs
|
||||
*
|
||||
* Author: Andy Fleming <afleming@freescale.com>
|
||||
* Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
|
||||
*
|
||||
* Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips)
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ucc.h>
|
||||
|
||||
#include "gianfar.h"
|
||||
#include "fsl_pq_mdio.h"
|
||||
|
||||
struct fsl_pq_mdio_priv {
|
||||
void __iomem *map;
|
||||
struct fsl_pq_mdio __iomem *regs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Write value to the PHY at mii_id at register regnum,
|
||||
* on the bus attached to the local interface, which may be different from the
|
||||
* generic mdio bus (tied to a single interface), waiting until the write is
|
||||
* done before returning. This is helpful in programming interfaces like
|
||||
* the TBI which control interfaces like onchip SERDES and are always tied to
|
||||
* the local mdio pins, which may not be the same as system mdio bus, used for
|
||||
* controlling the external PHYs, for example.
|
||||
*/
|
||||
int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
|
||||
int regnum, u16 value)
|
||||
{
|
||||
/* Set the PHY address and the register address we want to write */
|
||||
out_be32(®s->miimadd, (mii_id << 8) | regnum);
|
||||
|
||||
/* Write out the value we want */
|
||||
out_be32(®s->miimcon, value);
|
||||
|
||||
/* Wait for the transaction to finish */
|
||||
while (in_be32(®s->miimind) & MIIMIND_BUSY)
|
||||
cpu_relax();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the bus for PHY at addr mii_id, register regnum, and
|
||||
* return the value. Clears miimcom first. All PHY operation
|
||||
* done on the bus attached to the local interface,
|
||||
* which may be different from the generic mdio bus
|
||||
* This is helpful in programming interfaces like
|
||||
* the TBI which, in turn, control interfaces like onchip SERDES
|
||||
* and are always tied to the local mdio pins, which may not be the
|
||||
* same as system mdio bus, used for controlling the external PHYs, for eg.
|
||||
*/
|
||||
int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs,
|
||||
int mii_id, int regnum)
|
||||
{
|
||||
u16 value;
|
||||
|
||||
/* Set the PHY address and the register address we want to read */
|
||||
out_be32(®s->miimadd, (mii_id << 8) | regnum);
|
||||
|
||||
/* Clear miimcom, and then initiate a read */
|
||||
out_be32(®s->miimcom, 0);
|
||||
out_be32(®s->miimcom, MII_READ_COMMAND);
|
||||
|
||||
/* Wait for the transaction to finish */
|
||||
while (in_be32(®s->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
|
||||
cpu_relax();
|
||||
|
||||
/* Grab the value of the register from miimstat */
|
||||
value = in_be32(®s->miimstat);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
|
||||
{
|
||||
struct fsl_pq_mdio_priv *priv = bus->priv;
|
||||
|
||||
return priv->regs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write value to the PHY at mii_id at register regnum,
|
||||
* on the bus, waiting until the write is done before returning.
|
||||
*/
|
||||
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
|
||||
{
|
||||
struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
|
||||
|
||||
/* Write to the local MII regs */
|
||||
return fsl_pq_local_mdio_write(regs, mii_id, regnum, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the bus for PHY at addr mii_id, register regnum, and
|
||||
* return the value. Clears miimcom first.
|
||||
*/
|
||||
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
||||
{
|
||||
struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
|
||||
|
||||
/* Read the local MII regs */
|
||||
return fsl_pq_local_mdio_read(regs, mii_id, regnum);
|
||||
}
|
||||
|
||||
/* Reset the MIIM registers, and wait for the bus to free */
|
||||
static int fsl_pq_mdio_reset(struct mii_bus *bus)
|
||||
{
|
||||
struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
|
||||
int timeout = PHY_INIT_TIMEOUT;
|
||||
|
||||
mutex_lock(&bus->mdio_lock);
|
||||
|
||||
/* Reset the management interface */
|
||||
out_be32(®s->miimcfg, MIIMCFG_RESET);
|
||||
|
||||
/* Setup the MII Mgmt clock speed */
|
||||
out_be32(®s->miimcfg, MIIMCFG_INIT_VALUE);
|
||||
|
||||
/* Wait until the bus is free */
|
||||
while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--)
|
||||
cpu_relax();
|
||||
|
||||
mutex_unlock(&bus->mdio_lock);
|
||||
|
||||
if (timeout < 0) {
|
||||
printk(KERN_ERR "%s: The MII Bus is stuck!\n",
|
||||
bus->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsl_pq_mdio_bus_name(char *name, struct device_node *np)
|
||||
{
|
||||
const u32 *addr;
|
||||
u64 taddr = OF_BAD_ADDR;
|
||||
|
||||
addr = of_get_address(np, 0, NULL, NULL);
|
||||
if (addr)
|
||||
taddr = of_translate_address(np, addr);
|
||||
|
||||
snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name,
|
||||
(unsigned long long)taddr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_pq_mdio_bus_name);
|
||||
|
||||
/* Scan the bus in reverse, looking for an empty spot */
|
||||
static int fsl_pq_mdio_find_free(struct mii_bus *new_bus)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = PHY_MAX_ADDR; i > 0; i--) {
|
||||
u32 phy_id;
|
||||
|
||||
if (get_phy_id(new_bus, i, &phy_id))
|
||||
return -1;
|
||||
|
||||
if (phy_id == 0xffffffff)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
|
||||
static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np)
|
||||
{
|
||||
struct gfar __iomem *enet_regs;
|
||||
|
||||
/*
|
||||
* This is mildly evil, but so is our hardware for doing this.
|
||||
* Also, we have to cast back to struct gfar because of
|
||||
* definition weirdness done in gianfar.h.
|
||||
*/
|
||||
if(of_device_is_compatible(np, "fsl,gianfar-mdio") ||
|
||||
of_device_is_compatible(np, "fsl,gianfar-tbi") ||
|
||||
of_device_is_compatible(np, "gianfar")) {
|
||||
enet_regs = (struct gfar __iomem *)regs;
|
||||
return &enet_regs->tbipa;
|
||||
} else if (of_device_is_compatible(np, "fsl,etsec2-mdio") ||
|
||||
of_device_is_compatible(np, "fsl,etsec2-tbi")) {
|
||||
return of_iomap(np, 1);
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE)
|
||||
static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
int err = 0;
|
||||
|
||||
for_each_compatible_node(np, NULL, "ucc_geth") {
|
||||
struct resource tempres;
|
||||
|
||||
err = of_address_to_resource(np, 0, &tempres);
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
/* if our mdio regs fall within this UCC regs range */
|
||||
if ((start >= tempres.start) && (end <= tempres.end)) {
|
||||
/* Find the id of the UCC */
|
||||
const u32 *id;
|
||||
|
||||
id = of_get_property(np, "cell-index", NULL);
|
||||
if (!id) {
|
||||
id = of_get_property(np, "device-id", NULL);
|
||||
if (!id)
|
||||
continue;
|
||||
}
|
||||
|
||||
*ucc_id = *id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int fsl_pq_mdio_probe(struct platform_device *ofdev)
|
||||
{
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct device_node *tbi;
|
||||
struct fsl_pq_mdio_priv *priv;
|
||||
struct fsl_pq_mdio __iomem *regs = NULL;
|
||||
void __iomem *map;
|
||||
u32 __iomem *tbipa;
|
||||
struct mii_bus *new_bus;
|
||||
int tbiaddr = -1;
|
||||
const u32 *addrp;
|
||||
u64 addr = 0, size = 0;
|
||||
int err;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
new_bus = mdiobus_alloc();
|
||||
if (!new_bus) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_priv;
|
||||
}
|
||||
|
||||
new_bus->name = "Freescale PowerQUICC MII Bus",
|
||||
new_bus->read = &fsl_pq_mdio_read,
|
||||
new_bus->write = &fsl_pq_mdio_write,
|
||||
new_bus->reset = &fsl_pq_mdio_reset,
|
||||
new_bus->priv = priv;
|
||||
fsl_pq_mdio_bus_name(new_bus->id, np);
|
||||
|
||||
addrp = of_get_address(np, 0, &size, NULL);
|
||||
if (!addrp) {
|
||||
err = -EINVAL;
|
||||
goto err_free_bus;
|
||||
}
|
||||
|
||||
/* Set the PHY base address */
|
||||
addr = of_translate_address(np, addrp);
|
||||
if (addr == OF_BAD_ADDR) {
|
||||
err = -EINVAL;
|
||||
goto err_free_bus;
|
||||
}
|
||||
|
||||
map = ioremap(addr, size);
|
||||
if (!map) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_bus;
|
||||
}
|
||||
priv->map = map;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
|
||||
of_device_is_compatible(np, "fsl,gianfar-tbi") ||
|
||||
of_device_is_compatible(np, "fsl,ucc-mdio") ||
|
||||
of_device_is_compatible(np, "ucc_geth_phy"))
|
||||
map -= offsetof(struct fsl_pq_mdio, miimcfg);
|
||||
regs = map;
|
||||
priv->regs = regs;
|
||||
|
||||
new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
|
||||
|
||||
if (NULL == new_bus->irq) {
|
||||
err = -ENOMEM;
|
||||
goto err_unmap_regs;
|
||||
}
|
||||
|
||||
new_bus->parent = &ofdev->dev;
|
||||
dev_set_drvdata(&ofdev->dev, new_bus);
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
|
||||
of_device_is_compatible(np, "fsl,gianfar-tbi") ||
|
||||
of_device_is_compatible(np, "fsl,etsec2-mdio") ||
|
||||
of_device_is_compatible(np, "fsl,etsec2-tbi") ||
|
||||
of_device_is_compatible(np, "gianfar")) {
|
||||
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
|
||||
tbipa = get_gfar_tbipa(regs, np);
|
||||
if (!tbipa) {
|
||||
err = -EINVAL;
|
||||
goto err_free_irqs;
|
||||
}
|
||||
#else
|
||||
err = -ENODEV;
|
||||
goto err_free_irqs;
|
||||
#endif
|
||||
} else if (of_device_is_compatible(np, "fsl,ucc-mdio") ||
|
||||
of_device_is_compatible(np, "ucc_geth_phy")) {
|
||||
#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE)
|
||||
u32 id;
|
||||
static u32 mii_mng_master;
|
||||
|
||||
tbipa = ®s->utbipar;
|
||||
|
||||
if ((err = get_ucc_id_for_range(addr, addr + size, &id)))
|
||||
goto err_free_irqs;
|
||||
|
||||
if (!mii_mng_master) {
|
||||
mii_mng_master = id;
|
||||
ucc_set_qe_mux_mii_mng(id - 1);
|
||||
}
|
||||
#else
|
||||
err = -ENODEV;
|
||||
goto err_free_irqs;
|
||||
#endif
|
||||
} else {
|
||||
err = -ENODEV;
|
||||
goto err_free_irqs;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, tbi) {
|
||||
if (!strncmp(tbi->type, "tbi-phy", 8))
|
||||
break;
|
||||
}
|
||||
|
||||
if (tbi) {
|
||||
const u32 *prop = of_get_property(tbi, "reg", NULL);
|
||||
|
||||
if (prop)
|
||||
tbiaddr = *prop;
|
||||
}
|
||||
|
||||
if (tbiaddr == -1) {
|
||||
out_be32(tbipa, 0);
|
||||
|
||||
tbiaddr = fsl_pq_mdio_find_free(new_bus);
|
||||
}
|
||||
|
||||
/*
|
||||
* We define TBIPA at 0 to be illegal, opting to fail for boards that
|
||||
* have PHYs at 1-31, rather than change tbipa and rescan.
|
||||
*/
|
||||
if (tbiaddr == 0) {
|
||||
err = -EBUSY;
|
||||
|
||||
goto err_free_irqs;
|
||||
}
|
||||
|
||||
out_be32(tbipa, tbiaddr);
|
||||
|
||||
err = of_mdiobus_register(new_bus, np);
|
||||
if (err) {
|
||||
printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
|
||||
new_bus->name);
|
||||
goto err_free_irqs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irqs:
|
||||
kfree(new_bus->irq);
|
||||
err_unmap_regs:
|
||||
iounmap(priv->map);
|
||||
err_free_bus:
|
||||
kfree(new_bus);
|
||||
err_free_priv:
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int fsl_pq_mdio_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct device *device = &ofdev->dev;
|
||||
struct mii_bus *bus = dev_get_drvdata(device);
|
||||
struct fsl_pq_mdio_priv *priv = bus->priv;
|
||||
|
||||
mdiobus_unregister(bus);
|
||||
|
||||
dev_set_drvdata(device, NULL);
|
||||
|
||||
iounmap(priv->map);
|
||||
bus->priv = NULL;
|
||||
mdiobus_free(bus);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id fsl_pq_mdio_match[] = {
|
||||
{
|
||||
.type = "mdio",
|
||||
.compatible = "ucc_geth_phy",
|
||||
},
|
||||
{
|
||||
.type = "mdio",
|
||||
.compatible = "gianfar",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,ucc-mdio",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,gianfar-tbi",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,gianfar-mdio",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,etsec2-tbi",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,etsec2-mdio",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
|
||||
|
||||
static struct platform_driver fsl_pq_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "fsl-pq_mdio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = fsl_pq_mdio_match,
|
||||
},
|
||||
.probe = fsl_pq_mdio_probe,
|
||||
.remove = fsl_pq_mdio_remove,
|
||||
};
|
||||
|
||||
int __init fsl_pq_mdio_init(void)
|
||||
{
|
||||
return platform_driver_register(&fsl_pq_mdio_driver);
|
||||
}
|
||||
module_init(fsl_pq_mdio_init);
|
||||
|
||||
void fsl_pq_mdio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fsl_pq_mdio_driver);
|
||||
}
|
||||
module_exit(fsl_pq_mdio_exit);
|
||||
MODULE_LICENSE("GPL");
|
52
drivers/net/ethernet/freescale/fsl_pq_mdio.h
Normal file
52
drivers/net/ethernet/freescale/fsl_pq_mdio.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Freescale PowerQUICC MDIO Driver -- MII Management Bus Implementation
|
||||
* Driver for the MDIO bus controller on Freescale PowerQUICC processors
|
||||
*
|
||||
* Author: Andy Fleming
|
||||
* Modifier: Sandeep Gopalpet
|
||||
*
|
||||
* Copyright 2002-2004, 2008-2009 Freescale Semiconductor, 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 __FSL_PQ_MDIO_H
|
||||
#define __FSL_PQ_MDIO_H
|
||||
|
||||
#define MIIMIND_BUSY 0x00000001
|
||||
#define MIIMIND_NOTVALID 0x00000004
|
||||
#define MIIMCFG_INIT_VALUE 0x00000007
|
||||
#define MIIMCFG_RESET 0x80000000
|
||||
|
||||
#define MII_READ_COMMAND 0x00000001
|
||||
|
||||
struct fsl_pq_mdio {
|
||||
u8 res1[16];
|
||||
u32 ieventm; /* MDIO Interrupt event register (for etsec2)*/
|
||||
u32 imaskm; /* MDIO Interrupt mask register (for etsec2)*/
|
||||
u8 res2[4];
|
||||
u32 emapm; /* MDIO Event mapping register (for etsec2)*/
|
||||
u8 res3[1280];
|
||||
u32 miimcfg; /* MII management configuration reg */
|
||||
u32 miimcom; /* MII management command reg */
|
||||
u32 miimadd; /* MII management address reg */
|
||||
u32 miimcon; /* MII management control reg */
|
||||
u32 miimstat; /* MII management status reg */
|
||||
u32 miimind; /* MII management indication reg */
|
||||
u8 reserved[28]; /* Space holder */
|
||||
u32 utbipar; /* TBI phy address reg (only on UCC) */
|
||||
u8 res4[2728];
|
||||
} __packed;
|
||||
|
||||
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
|
||||
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
|
||||
int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
|
||||
int regnum, u16 value);
|
||||
int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs, int mii_id, int regnum);
|
||||
int __init fsl_pq_mdio_init(void);
|
||||
void fsl_pq_mdio_exit(void);
|
||||
void fsl_pq_mdio_bus_name(char *name, struct device_node *np);
|
||||
#endif /* FSL_PQ_MDIO_H */
|
3291
drivers/net/ethernet/freescale/gianfar.c
Normal file
3291
drivers/net/ethernet/freescale/gianfar.c
Normal file
File diff suppressed because it is too large
Load Diff
1216
drivers/net/ethernet/freescale/gianfar.h
Normal file
1216
drivers/net/ethernet/freescale/gianfar.h
Normal file
File diff suppressed because it is too large
Load Diff
1747
drivers/net/ethernet/freescale/gianfar_ethtool.c
Normal file
1747
drivers/net/ethernet/freescale/gianfar_ethtool.c
Normal file
File diff suppressed because it is too large
Load Diff
583
drivers/net/ethernet/freescale/gianfar_ptp.c
Normal file
583
drivers/net/ethernet/freescale/gianfar_ptp.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* PTP 1588 clock using the eTSEC
|
||||
*
|
||||
* Copyright (C) 2010 OMICRON electronics GmbH
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
#include "gianfar.h"
|
||||
|
||||
/*
|
||||
* gianfar ptp registers
|
||||
* Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010
|
||||
*/
|
||||
struct gianfar_ptp_registers {
|
||||
u32 tmr_ctrl; /* Timer control register */
|
||||
u32 tmr_tevent; /* Timestamp event register */
|
||||
u32 tmr_temask; /* Timer event mask register */
|
||||
u32 tmr_pevent; /* Timestamp event register */
|
||||
u32 tmr_pemask; /* Timer event mask register */
|
||||
u32 tmr_stat; /* Timestamp status register */
|
||||
u32 tmr_cnt_h; /* Timer counter high register */
|
||||
u32 tmr_cnt_l; /* Timer counter low register */
|
||||
u32 tmr_add; /* Timer drift compensation addend register */
|
||||
u32 tmr_acc; /* Timer accumulator register */
|
||||
u32 tmr_prsc; /* Timer prescale */
|
||||
u8 res1[4];
|
||||
u32 tmroff_h; /* Timer offset high */
|
||||
u32 tmroff_l; /* Timer offset low */
|
||||
u8 res2[8];
|
||||
u32 tmr_alarm1_h; /* Timer alarm 1 high register */
|
||||
u32 tmr_alarm1_l; /* Timer alarm 1 high register */
|
||||
u32 tmr_alarm2_h; /* Timer alarm 2 high register */
|
||||
u32 tmr_alarm2_l; /* Timer alarm 2 high register */
|
||||
u8 res3[48];
|
||||
u32 tmr_fiper1; /* Timer fixed period interval */
|
||||
u32 tmr_fiper2; /* Timer fixed period interval */
|
||||
u32 tmr_fiper3; /* Timer fixed period interval */
|
||||
u8 res4[20];
|
||||
u32 tmr_etts1_h; /* Timestamp of general purpose external trigger */
|
||||
u32 tmr_etts1_l; /* Timestamp of general purpose external trigger */
|
||||
u32 tmr_etts2_h; /* Timestamp of general purpose external trigger */
|
||||
u32 tmr_etts2_l; /* Timestamp of general purpose external trigger */
|
||||
};
|
||||
|
||||
/* Bit definitions for the TMR_CTRL register */
|
||||
#define ALM1P (1<<31) /* Alarm1 output polarity */
|
||||
#define ALM2P (1<<30) /* Alarm2 output polarity */
|
||||
#define FS (1<<28) /* FIPER start indication */
|
||||
#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */
|
||||
#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */
|
||||
#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */
|
||||
#define TCLK_PERIOD_MASK (0x3ff)
|
||||
#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */
|
||||
#define FRD (1<<14) /* FIPER Realignment Disable */
|
||||
#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */
|
||||
#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */
|
||||
#define ETEP2 (1<<9) /* External trigger 2 edge polarity */
|
||||
#define ETEP1 (1<<8) /* External trigger 1 edge polarity */
|
||||
#define COPH (1<<7) /* Generated clock output phase. */
|
||||
#define CIPH (1<<6) /* External oscillator input clock phase */
|
||||
#define TMSR (1<<5) /* Timer soft reset. */
|
||||
#define BYP (1<<3) /* Bypass drift compensated clock */
|
||||
#define TE (1<<2) /* 1588 timer enable. */
|
||||
#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source */
|
||||
#define CKSEL_MASK (0x3)
|
||||
|
||||
/* Bit definitions for the TMR_TEVENT register */
|
||||
#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */
|
||||
#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */
|
||||
#define ALM2 (1<<17) /* Current time = alarm time register 2 */
|
||||
#define ALM1 (1<<16) /* Current time = alarm time register 1 */
|
||||
#define PP1 (1<<7) /* periodic pulse generated on FIPER1 */
|
||||
#define PP2 (1<<6) /* periodic pulse generated on FIPER2 */
|
||||
#define PP3 (1<<5) /* periodic pulse generated on FIPER3 */
|
||||
|
||||
/* Bit definitions for the TMR_TEMASK register */
|
||||
#define ETS2EN (1<<25) /* External trigger 2 timestamp enable */
|
||||
#define ETS1EN (1<<24) /* External trigger 1 timestamp enable */
|
||||
#define ALM2EN (1<<17) /* Timer ALM2 event enable */
|
||||
#define ALM1EN (1<<16) /* Timer ALM1 event enable */
|
||||
#define PP1EN (1<<7) /* Periodic pulse event 1 enable */
|
||||
#define PP2EN (1<<6) /* Periodic pulse event 2 enable */
|
||||
|
||||
/* Bit definitions for the TMR_PEVENT register */
|
||||
#define TXP2 (1<<9) /* PTP transmitted timestamp im TXTS2 */
|
||||
#define TXP1 (1<<8) /* PTP transmitted timestamp in TXTS1 */
|
||||
#define RXP (1<<0) /* PTP frame has been received */
|
||||
|
||||
/* Bit definitions for the TMR_PEMASK register */
|
||||
#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */
|
||||
#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */
|
||||
#define RXPEN (1<<0) /* Receive PTP packet event enable */
|
||||
|
||||
/* Bit definitions for the TMR_STAT register */
|
||||
#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */
|
||||
#define STAT_VEC_MASK (0x3f)
|
||||
|
||||
/* Bit definitions for the TMR_PRSC register */
|
||||
#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */
|
||||
#define PRSC_OCK_MASK (0xffff)
|
||||
|
||||
|
||||
#define DRIVER "gianfar_ptp"
|
||||
#define DEFAULT_CKSEL 1
|
||||
#define N_ALARM 1 /* first alarm is used internally to reset fipers */
|
||||
#define N_EXT_TS 2
|
||||
#define REG_SIZE sizeof(struct gianfar_ptp_registers)
|
||||
|
||||
struct etsects {
|
||||
struct gianfar_ptp_registers *regs;
|
||||
spinlock_t lock; /* protects regs */
|
||||
struct ptp_clock *clock;
|
||||
struct ptp_clock_info caps;
|
||||
struct resource *rsrc;
|
||||
int irq;
|
||||
u64 alarm_interval; /* for periodic alarm */
|
||||
u64 alarm_value;
|
||||
u32 tclk_period; /* nanoseconds */
|
||||
u32 tmr_prsc;
|
||||
u32 tmr_add;
|
||||
u32 cksel;
|
||||
u32 tmr_fiper1;
|
||||
u32 tmr_fiper2;
|
||||
};
|
||||
|
||||
/*
|
||||
* Register access functions
|
||||
*/
|
||||
|
||||
/* Caller must hold etsects->lock. */
|
||||
static u64 tmr_cnt_read(struct etsects *etsects)
|
||||
{
|
||||
u64 ns;
|
||||
u32 lo, hi;
|
||||
|
||||
lo = gfar_read(&etsects->regs->tmr_cnt_l);
|
||||
hi = gfar_read(&etsects->regs->tmr_cnt_h);
|
||||
ns = ((u64) hi) << 32;
|
||||
ns |= lo;
|
||||
return ns;
|
||||
}
|
||||
|
||||
/* Caller must hold etsects->lock. */
|
||||
static void tmr_cnt_write(struct etsects *etsects, u64 ns)
|
||||
{
|
||||
u32 hi = ns >> 32;
|
||||
u32 lo = ns & 0xffffffff;
|
||||
|
||||
gfar_write(&etsects->regs->tmr_cnt_l, lo);
|
||||
gfar_write(&etsects->regs->tmr_cnt_h, hi);
|
||||
}
|
||||
|
||||
/* Caller must hold etsects->lock. */
|
||||
static void set_alarm(struct etsects *etsects)
|
||||
{
|
||||
u64 ns;
|
||||
u32 lo, hi;
|
||||
|
||||
ns = tmr_cnt_read(etsects) + 1500000000ULL;
|
||||
ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
|
||||
ns -= etsects->tclk_period;
|
||||
hi = ns >> 32;
|
||||
lo = ns & 0xffffffff;
|
||||
gfar_write(&etsects->regs->tmr_alarm1_l, lo);
|
||||
gfar_write(&etsects->regs->tmr_alarm1_h, hi);
|
||||
}
|
||||
|
||||
/* Caller must hold etsects->lock. */
|
||||
static void set_fipers(struct etsects *etsects)
|
||||
{
|
||||
set_alarm(etsects);
|
||||
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
|
||||
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt service routine
|
||||
*/
|
||||
|
||||
static irqreturn_t isr(int irq, void *priv)
|
||||
{
|
||||
struct etsects *etsects = priv;
|
||||
struct ptp_clock_event event;
|
||||
u64 ns;
|
||||
u32 ack = 0, lo, hi, mask, val;
|
||||
|
||||
val = gfar_read(&etsects->regs->tmr_tevent);
|
||||
|
||||
if (val & ETS1) {
|
||||
ack |= ETS1;
|
||||
hi = gfar_read(&etsects->regs->tmr_etts1_h);
|
||||
lo = gfar_read(&etsects->regs->tmr_etts1_l);
|
||||
event.type = PTP_CLOCK_EXTTS;
|
||||
event.index = 0;
|
||||
event.timestamp = ((u64) hi) << 32;
|
||||
event.timestamp |= lo;
|
||||
ptp_clock_event(etsects->clock, &event);
|
||||
}
|
||||
|
||||
if (val & ETS2) {
|
||||
ack |= ETS2;
|
||||
hi = gfar_read(&etsects->regs->tmr_etts2_h);
|
||||
lo = gfar_read(&etsects->regs->tmr_etts2_l);
|
||||
event.type = PTP_CLOCK_EXTTS;
|
||||
event.index = 1;
|
||||
event.timestamp = ((u64) hi) << 32;
|
||||
event.timestamp |= lo;
|
||||
ptp_clock_event(etsects->clock, &event);
|
||||
}
|
||||
|
||||
if (val & ALM2) {
|
||||
ack |= ALM2;
|
||||
if (etsects->alarm_value) {
|
||||
event.type = PTP_CLOCK_ALARM;
|
||||
event.index = 0;
|
||||
event.timestamp = etsects->alarm_value;
|
||||
ptp_clock_event(etsects->clock, &event);
|
||||
}
|
||||
if (etsects->alarm_interval) {
|
||||
ns = etsects->alarm_value + etsects->alarm_interval;
|
||||
hi = ns >> 32;
|
||||
lo = ns & 0xffffffff;
|
||||
spin_lock(&etsects->lock);
|
||||
gfar_write(&etsects->regs->tmr_alarm2_l, lo);
|
||||
gfar_write(&etsects->regs->tmr_alarm2_h, hi);
|
||||
spin_unlock(&etsects->lock);
|
||||
etsects->alarm_value = ns;
|
||||
} else {
|
||||
gfar_write(&etsects->regs->tmr_tevent, ALM2);
|
||||
spin_lock(&etsects->lock);
|
||||
mask = gfar_read(&etsects->regs->tmr_temask);
|
||||
mask &= ~ALM2EN;
|
||||
gfar_write(&etsects->regs->tmr_temask, mask);
|
||||
spin_unlock(&etsects->lock);
|
||||
etsects->alarm_value = 0;
|
||||
etsects->alarm_interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (val & PP1) {
|
||||
ack |= PP1;
|
||||
event.type = PTP_CLOCK_PPS;
|
||||
ptp_clock_event(etsects->clock, &event);
|
||||
}
|
||||
|
||||
if (ack) {
|
||||
gfar_write(&etsects->regs->tmr_tevent, ack);
|
||||
return IRQ_HANDLED;
|
||||
} else
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* PTP clock operations
|
||||
*/
|
||||
|
||||
static int ptp_gianfar_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
|
||||
{
|
||||
u64 adj;
|
||||
u32 diff, tmr_add;
|
||||
int neg_adj = 0;
|
||||
struct etsects *etsects = container_of(ptp, struct etsects, caps);
|
||||
|
||||
if (ppb < 0) {
|
||||
neg_adj = 1;
|
||||
ppb = -ppb;
|
||||
}
|
||||
tmr_add = etsects->tmr_add;
|
||||
adj = tmr_add;
|
||||
adj *= ppb;
|
||||
diff = div_u64(adj, 1000000000ULL);
|
||||
|
||||
tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
|
||||
|
||||
gfar_write(&etsects->regs->tmr_add, tmr_add);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_gianfar_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
s64 now;
|
||||
unsigned long flags;
|
||||
struct etsects *etsects = container_of(ptp, struct etsects, caps);
|
||||
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
|
||||
now = tmr_cnt_read(etsects);
|
||||
now += delta;
|
||||
tmr_cnt_write(etsects, now);
|
||||
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
|
||||
set_fipers(etsects);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_gianfar_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
|
||||
{
|
||||
u64 ns;
|
||||
u32 remainder;
|
||||
unsigned long flags;
|
||||
struct etsects *etsects = container_of(ptp, struct etsects, caps);
|
||||
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
|
||||
ns = tmr_cnt_read(etsects);
|
||||
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
|
||||
ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
|
||||
ts->tv_nsec = remainder;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_gianfar_settime(struct ptp_clock_info *ptp,
|
||||
const struct timespec *ts)
|
||||
{
|
||||
u64 ns;
|
||||
unsigned long flags;
|
||||
struct etsects *etsects = container_of(ptp, struct etsects, caps);
|
||||
|
||||
ns = ts->tv_sec * 1000000000ULL;
|
||||
ns += ts->tv_nsec;
|
||||
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
|
||||
tmr_cnt_write(etsects, ns);
|
||||
set_fipers(etsects);
|
||||
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_gianfar_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on)
|
||||
{
|
||||
struct etsects *etsects = container_of(ptp, struct etsects, caps);
|
||||
unsigned long flags;
|
||||
u32 bit, mask;
|
||||
|
||||
switch (rq->type) {
|
||||
case PTP_CLK_REQ_EXTTS:
|
||||
switch (rq->extts.index) {
|
||||
case 0:
|
||||
bit = ETS1EN;
|
||||
break;
|
||||
case 1:
|
||||
bit = ETS2EN;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
mask = gfar_read(&etsects->regs->tmr_temask);
|
||||
if (on)
|
||||
mask |= bit;
|
||||
else
|
||||
mask &= ~bit;
|
||||
gfar_write(&etsects->regs->tmr_temask, mask);
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
return 0;
|
||||
|
||||
case PTP_CLK_REQ_PPS:
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
mask = gfar_read(&etsects->regs->tmr_temask);
|
||||
if (on)
|
||||
mask |= PP1EN;
|
||||
else
|
||||
mask &= ~PP1EN;
|
||||
gfar_write(&etsects->regs->tmr_temask, mask);
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static struct ptp_clock_info ptp_gianfar_caps = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "gianfar clock",
|
||||
.max_adj = 512000,
|
||||
.n_alarm = N_ALARM,
|
||||
.n_ext_ts = N_EXT_TS,
|
||||
.n_per_out = 0,
|
||||
.pps = 1,
|
||||
.adjfreq = ptp_gianfar_adjfreq,
|
||||
.adjtime = ptp_gianfar_adjtime,
|
||||
.gettime = ptp_gianfar_gettime,
|
||||
.settime = ptp_gianfar_settime,
|
||||
.enable = ptp_gianfar_enable,
|
||||
};
|
||||
|
||||
/* OF device tree */
|
||||
|
||||
static int get_of_u32(struct device_node *node, char *str, u32 *val)
|
||||
{
|
||||
int plen;
|
||||
const u32 *prop = of_get_property(node, str, &plen);
|
||||
|
||||
if (!prop || plen != sizeof(*prop))
|
||||
return -1;
|
||||
*val = *prop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gianfar_ptp_probe(struct platform_device *dev)
|
||||
{
|
||||
struct device_node *node = dev->dev.of_node;
|
||||
struct etsects *etsects;
|
||||
struct timespec now;
|
||||
int err = -ENOMEM;
|
||||
u32 tmr_ctrl;
|
||||
unsigned long flags;
|
||||
|
||||
etsects = kzalloc(sizeof(*etsects), GFP_KERNEL);
|
||||
if (!etsects)
|
||||
goto no_memory;
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
etsects->caps = ptp_gianfar_caps;
|
||||
etsects->cksel = DEFAULT_CKSEL;
|
||||
|
||||
if (get_of_u32(node, "fsl,tclk-period", &etsects->tclk_period) ||
|
||||
get_of_u32(node, "fsl,tmr-prsc", &etsects->tmr_prsc) ||
|
||||
get_of_u32(node, "fsl,tmr-add", &etsects->tmr_add) ||
|
||||
get_of_u32(node, "fsl,tmr-fiper1", &etsects->tmr_fiper1) ||
|
||||
get_of_u32(node, "fsl,tmr-fiper2", &etsects->tmr_fiper2) ||
|
||||
get_of_u32(node, "fsl,max-adj", &etsects->caps.max_adj)) {
|
||||
pr_err("device tree node missing required elements\n");
|
||||
goto no_node;
|
||||
}
|
||||
|
||||
etsects->irq = platform_get_irq(dev, 0);
|
||||
|
||||
if (etsects->irq == NO_IRQ) {
|
||||
pr_err("irq not in device tree\n");
|
||||
goto no_node;
|
||||
}
|
||||
if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) {
|
||||
pr_err("request_irq failed\n");
|
||||
goto no_node;
|
||||
}
|
||||
|
||||
etsects->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!etsects->rsrc) {
|
||||
pr_err("no resource\n");
|
||||
goto no_resource;
|
||||
}
|
||||
if (request_resource(&ioport_resource, etsects->rsrc)) {
|
||||
pr_err("resource busy\n");
|
||||
goto no_resource;
|
||||
}
|
||||
|
||||
spin_lock_init(&etsects->lock);
|
||||
|
||||
etsects->regs = ioremap(etsects->rsrc->start,
|
||||
resource_size(etsects->rsrc));
|
||||
if (!etsects->regs) {
|
||||
pr_err("ioremap ptp registers failed\n");
|
||||
goto no_ioremap;
|
||||
}
|
||||
getnstimeofday(&now);
|
||||
ptp_gianfar_settime(&etsects->caps, &now);
|
||||
|
||||
tmr_ctrl =
|
||||
(etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
|
||||
(etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
|
||||
|
||||
spin_lock_irqsave(&etsects->lock, flags);
|
||||
|
||||
gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl);
|
||||
gfar_write(&etsects->regs->tmr_add, etsects->tmr_add);
|
||||
gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc);
|
||||
gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
|
||||
gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
|
||||
set_alarm(etsects);
|
||||
gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FS|RTPE|TE|FRD);
|
||||
|
||||
spin_unlock_irqrestore(&etsects->lock, flags);
|
||||
|
||||
etsects->clock = ptp_clock_register(&etsects->caps);
|
||||
if (IS_ERR(etsects->clock)) {
|
||||
err = PTR_ERR(etsects->clock);
|
||||
goto no_clock;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&dev->dev, etsects);
|
||||
|
||||
return 0;
|
||||
|
||||
no_clock:
|
||||
no_ioremap:
|
||||
release_resource(etsects->rsrc);
|
||||
no_resource:
|
||||
free_irq(etsects->irq, etsects);
|
||||
no_node:
|
||||
kfree(etsects);
|
||||
no_memory:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gianfar_ptp_remove(struct platform_device *dev)
|
||||
{
|
||||
struct etsects *etsects = dev_get_drvdata(&dev->dev);
|
||||
|
||||
gfar_write(&etsects->regs->tmr_temask, 0);
|
||||
gfar_write(&etsects->regs->tmr_ctrl, 0);
|
||||
|
||||
ptp_clock_unregister(etsects->clock);
|
||||
iounmap(etsects->regs);
|
||||
release_resource(etsects->rsrc);
|
||||
free_irq(etsects->irq, etsects);
|
||||
kfree(etsects);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id match_table[] = {
|
||||
{ .compatible = "fsl,etsec-ptp" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver gianfar_ptp_driver = {
|
||||
.driver = {
|
||||
.name = "gianfar_ptp",
|
||||
.of_match_table = match_table,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = gianfar_ptp_probe,
|
||||
.remove = gianfar_ptp_remove,
|
||||
};
|
||||
|
||||
/* module operations */
|
||||
|
||||
static int __init ptp_gianfar_init(void)
|
||||
{
|
||||
return platform_driver_register(&gianfar_ptp_driver);
|
||||
}
|
||||
|
||||
module_init(ptp_gianfar_init);
|
||||
|
||||
static void __exit ptp_gianfar_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gianfar_ptp_driver);
|
||||
}
|
||||
|
||||
module_exit(ptp_gianfar_exit);
|
||||
|
||||
MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
|
||||
MODULE_DESCRIPTION("PTP clock using the eTSEC");
|
||||
MODULE_LICENSE("GPL");
|
341
drivers/net/ethernet/freescale/gianfar_sysfs.c
Normal file
341
drivers/net/ethernet/freescale/gianfar_sysfs.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* drivers/net/gianfar_sysfs.c
|
||||
*
|
||||
* Gianfar Ethernet Driver
|
||||
* This driver is designed for the non-CPM ethernet controllers
|
||||
* on the 85xx and 83xx family of integrated processors
|
||||
* Based on 8260_io/fcc_enet.c
|
||||
*
|
||||
* Author: Andy Fleming
|
||||
* Maintainer: Kumar Gala (galak@kernel.crashing.org)
|
||||
* Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
|
||||
*
|
||||
* Copyright 2002-2009 Freescale Semiconductor, 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.
|
||||
*
|
||||
* Sysfs file creation and management
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "gianfar.h"
|
||||
|
||||
static ssize_t gfar_show_bd_stash(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%s\n", priv->bd_stash_en ? "on" : "off");
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_bd_stash(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
int new_setting = 0;
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
|
||||
return count;
|
||||
|
||||
|
||||
/* Find out the new setting */
|
||||
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
|
||||
new_setting = 1;
|
||||
else if (!strncmp("off", buf, count - 1) ||
|
||||
!strncmp("0", buf, count - 1))
|
||||
new_setting = 0;
|
||||
else
|
||||
return count;
|
||||
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_rx_qs(priv);
|
||||
|
||||
/* Set the new stashing value */
|
||||
priv->bd_stash_en = new_setting;
|
||||
|
||||
temp = gfar_read(®s->attr);
|
||||
|
||||
if (new_setting)
|
||||
temp |= ATTR_BDSTASH;
|
||||
else
|
||||
temp &= ~(ATTR_BDSTASH);
|
||||
|
||||
gfar_write(®s->attr, temp);
|
||||
|
||||
unlock_rx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bd_stash, 0644, gfar_show_bd_stash, gfar_set_bd_stash);
|
||||
|
||||
static ssize_t gfar_show_rx_stash_size(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", priv->rx_stash_size);
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_rx_stash_size(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
unsigned int length = simple_strtoul(buf, NULL, 0);
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
|
||||
return count;
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_rx_qs(priv);
|
||||
|
||||
if (length > priv->rx_buffer_size)
|
||||
goto out;
|
||||
|
||||
if (length == priv->rx_stash_size)
|
||||
goto out;
|
||||
|
||||
priv->rx_stash_size = length;
|
||||
|
||||
temp = gfar_read(®s->attreli);
|
||||
temp &= ~ATTRELI_EL_MASK;
|
||||
temp |= ATTRELI_EL(length);
|
||||
gfar_write(®s->attreli, temp);
|
||||
|
||||
/* Turn stashing on/off as appropriate */
|
||||
temp = gfar_read(®s->attr);
|
||||
|
||||
if (length)
|
||||
temp |= ATTR_BUFSTASH;
|
||||
else
|
||||
temp &= ~(ATTR_BUFSTASH);
|
||||
|
||||
gfar_write(®s->attr, temp);
|
||||
|
||||
out:
|
||||
unlock_rx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_stash_size, 0644, gfar_show_rx_stash_size,
|
||||
gfar_set_rx_stash_size);
|
||||
|
||||
/* Stashing will only be enabled when rx_stash_size != 0 */
|
||||
static ssize_t gfar_show_rx_stash_index(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", priv->rx_stash_index);
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_rx_stash_index(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
unsigned short index = simple_strtoul(buf, NULL, 0);
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
|
||||
return count;
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_rx_qs(priv);
|
||||
|
||||
if (index > priv->rx_stash_size)
|
||||
goto out;
|
||||
|
||||
if (index == priv->rx_stash_index)
|
||||
goto out;
|
||||
|
||||
priv->rx_stash_index = index;
|
||||
|
||||
temp = gfar_read(®s->attreli);
|
||||
temp &= ~ATTRELI_EI_MASK;
|
||||
temp |= ATTRELI_EI(index);
|
||||
gfar_write(®s->attreli, temp);
|
||||
|
||||
out:
|
||||
unlock_rx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(rx_stash_index, 0644, gfar_show_rx_stash_index,
|
||||
gfar_set_rx_stash_index);
|
||||
|
||||
static ssize_t gfar_show_fifo_threshold(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", priv->fifo_threshold);
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_fifo_threshold(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
unsigned int length = simple_strtoul(buf, NULL, 0);
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (length > GFAR_MAX_FIFO_THRESHOLD)
|
||||
return count;
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_tx_qs(priv);
|
||||
|
||||
priv->fifo_threshold = length;
|
||||
|
||||
temp = gfar_read(®s->fifo_tx_thr);
|
||||
temp &= ~FIFO_TX_THR_MASK;
|
||||
temp |= length;
|
||||
gfar_write(®s->fifo_tx_thr, temp);
|
||||
|
||||
unlock_tx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fifo_threshold, 0644, gfar_show_fifo_threshold,
|
||||
gfar_set_fifo_threshold);
|
||||
|
||||
static ssize_t gfar_show_fifo_starve(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", priv->fifo_starve);
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_fifo_starve(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
unsigned int num = simple_strtoul(buf, NULL, 0);
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (num > GFAR_MAX_FIFO_STARVE)
|
||||
return count;
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_tx_qs(priv);
|
||||
|
||||
priv->fifo_starve = num;
|
||||
|
||||
temp = gfar_read(®s->fifo_tx_starve);
|
||||
temp &= ~FIFO_TX_STARVE_MASK;
|
||||
temp |= num;
|
||||
gfar_write(®s->fifo_tx_starve, temp);
|
||||
|
||||
unlock_tx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fifo_starve, 0644, gfar_show_fifo_starve,
|
||||
gfar_set_fifo_starve);
|
||||
|
||||
static ssize_t gfar_show_fifo_starve_off(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
|
||||
return sprintf(buf, "%d\n", priv->fifo_starve_off);
|
||||
}
|
||||
|
||||
static ssize_t gfar_set_fifo_starve_off(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
|
||||
struct gfar __iomem *regs = priv->gfargrp[0].regs;
|
||||
unsigned int num = simple_strtoul(buf, NULL, 0);
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
|
||||
if (num > GFAR_MAX_FIFO_STARVE_OFF)
|
||||
return count;
|
||||
|
||||
local_irq_save(flags);
|
||||
lock_tx_qs(priv);
|
||||
|
||||
priv->fifo_starve_off = num;
|
||||
|
||||
temp = gfar_read(®s->fifo_tx_starve_shutoff);
|
||||
temp &= ~FIFO_TX_STARVE_OFF_MASK;
|
||||
temp |= num;
|
||||
gfar_write(®s->fifo_tx_starve_shutoff, temp);
|
||||
|
||||
unlock_tx_qs(priv);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fifo_starve_off, 0644, gfar_show_fifo_starve_off,
|
||||
gfar_set_fifo_starve_off);
|
||||
|
||||
void gfar_init_sysfs(struct net_device *dev)
|
||||
{
|
||||
struct gfar_private *priv = netdev_priv(dev);
|
||||
int rc;
|
||||
|
||||
/* Initialize the default values */
|
||||
priv->fifo_threshold = DEFAULT_FIFO_TX_THR;
|
||||
priv->fifo_starve = DEFAULT_FIFO_TX_STARVE;
|
||||
priv->fifo_starve_off = DEFAULT_FIFO_TX_STARVE_OFF;
|
||||
|
||||
/* Create our sysfs files */
|
||||
rc = device_create_file(&dev->dev, &dev_attr_bd_stash);
|
||||
rc |= device_create_file(&dev->dev, &dev_attr_rx_stash_size);
|
||||
rc |= device_create_file(&dev->dev, &dev_attr_rx_stash_index);
|
||||
rc |= device_create_file(&dev->dev, &dev_attr_fifo_threshold);
|
||||
rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve);
|
||||
rc |= device_create_file(&dev->dev, &dev_attr_fifo_starve_off);
|
||||
if (rc)
|
||||
dev_err(&dev->dev, "Error creating gianfar sysfs files.\n");
|
||||
}
|
4026
drivers/net/ethernet/freescale/ucc_geth.c
Normal file
4026
drivers/net/ethernet/freescale/ucc_geth.c
Normal file
File diff suppressed because it is too large
Load Diff
1240
drivers/net/ethernet/freescale/ucc_geth.h
Normal file
1240
drivers/net/ethernet/freescale/ucc_geth.h
Normal file
File diff suppressed because it is too large
Load Diff
423
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
Normal file
423
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* Description: QE UCC Gigabit Ethernet Ethtool API Set
|
||||
*
|
||||
* Author: Li Yang <leoli@freescale.com>
|
||||
*
|
||||
* Limitation:
|
||||
* Can only get/set settings of the first queue.
|
||||
* Need to re-open the interface manually after changing some parameters.
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/types.h>
|
||||
|
||||
#include "ucc_geth.h"
|
||||
|
||||
static char hw_stat_gstrings[][ETH_GSTRING_LEN] = {
|
||||
"tx-64-frames",
|
||||
"tx-65-127-frames",
|
||||
"tx-128-255-frames",
|
||||
"rx-64-frames",
|
||||
"rx-65-127-frames",
|
||||
"rx-128-255-frames",
|
||||
"tx-bytes-ok",
|
||||
"tx-pause-frames",
|
||||
"tx-multicast-frames",
|
||||
"tx-broadcast-frames",
|
||||
"rx-frames",
|
||||
"rx-bytes-ok",
|
||||
"rx-bytes-all",
|
||||
"rx-multicast-frames",
|
||||
"rx-broadcast-frames",
|
||||
"stats-counter-carry",
|
||||
"stats-counter-mask",
|
||||
"rx-dropped-frames",
|
||||
};
|
||||
|
||||
static char tx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
|
||||
"tx-single-collision",
|
||||
"tx-multiple-collision",
|
||||
"tx-late-collsion",
|
||||
"tx-aborted-frames",
|
||||
"tx-lost-frames",
|
||||
"tx-carrier-sense-errors",
|
||||
"tx-frames-ok",
|
||||
"tx-excessive-differ-frames",
|
||||
"tx-256-511-frames",
|
||||
"tx-512-1023-frames",
|
||||
"tx-1024-1518-frames",
|
||||
"tx-jumbo-frames",
|
||||
};
|
||||
|
||||
static char rx_fw_stat_gstrings[][ETH_GSTRING_LEN] = {
|
||||
"rx-crc-errors",
|
||||
"rx-alignment-errors",
|
||||
"rx-in-range-length-errors",
|
||||
"rx-out-of-range-length-errors",
|
||||
"rx-too-long-frames",
|
||||
"rx-runt",
|
||||
"rx-very-long-event",
|
||||
"rx-symbol-errors",
|
||||
"rx-busy-drop-frames",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"rx-mismatch-drop-frames",
|
||||
"rx-small-than-64",
|
||||
"rx-256-511-frames",
|
||||
"rx-512-1023-frames",
|
||||
"rx-1024-1518-frames",
|
||||
"rx-jumbo-frames",
|
||||
"rx-mac-error-loss",
|
||||
"rx-pause-frames",
|
||||
"reserved",
|
||||
"rx-vlan-removed",
|
||||
"rx-vlan-replaced",
|
||||
"rx-vlan-inserted",
|
||||
"rx-ip-checksum-errors",
|
||||
};
|
||||
|
||||
#define UEC_HW_STATS_LEN ARRAY_SIZE(hw_stat_gstrings)
|
||||
#define UEC_TX_FW_STATS_LEN ARRAY_SIZE(tx_fw_stat_gstrings)
|
||||
#define UEC_RX_FW_STATS_LEN ARRAY_SIZE(rx_fw_stat_gstrings)
|
||||
|
||||
static int
|
||||
uec_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct phy_device *phydev = ugeth->phydev;
|
||||
struct ucc_geth_info *ug_info = ugeth->ug_info;
|
||||
|
||||
if (!phydev)
|
||||
return -ENODEV;
|
||||
|
||||
ecmd->maxtxpkt = 1;
|
||||
ecmd->maxrxpkt = ug_info->interruptcoalescingmaxvalue[0];
|
||||
|
||||
return phy_ethtool_gset(phydev, ecmd);
|
||||
}
|
||||
|
||||
static int
|
||||
uec_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct phy_device *phydev = ugeth->phydev;
|
||||
|
||||
if (!phydev)
|
||||
return -ENODEV;
|
||||
|
||||
return phy_ethtool_sset(phydev, ecmd);
|
||||
}
|
||||
|
||||
static void
|
||||
uec_get_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
|
||||
pause->autoneg = ugeth->phydev->autoneg;
|
||||
|
||||
if (ugeth->ug_info->receiveFlowControl)
|
||||
pause->rx_pause = 1;
|
||||
if (ugeth->ug_info->transmitFlowControl)
|
||||
pause->tx_pause = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
uec_set_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
int ret = 0;
|
||||
|
||||
ugeth->ug_info->receiveFlowControl = pause->rx_pause;
|
||||
ugeth->ug_info->transmitFlowControl = pause->tx_pause;
|
||||
|
||||
if (ugeth->phydev->autoneg) {
|
||||
if (netif_running(netdev)) {
|
||||
/* FIXME: automatically restart */
|
||||
printk(KERN_INFO
|
||||
"Please re-open the interface.\n");
|
||||
}
|
||||
} else {
|
||||
struct ucc_geth_info *ug_info = ugeth->ug_info;
|
||||
|
||||
ret = init_flow_control_params(ug_info->aufc,
|
||||
ug_info->receiveFlowControl,
|
||||
ug_info->transmitFlowControl,
|
||||
ug_info->pausePeriod,
|
||||
ug_info->extensionField,
|
||||
&ugeth->uccf->uf_regs->upsmr,
|
||||
&ugeth->ug_regs->uempr,
|
||||
&ugeth->ug_regs->maccfg1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
uec_get_msglevel(struct net_device *netdev)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
return ugeth->msg_enable;
|
||||
}
|
||||
|
||||
static void
|
||||
uec_set_msglevel(struct net_device *netdev, uint32_t data)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
ugeth->msg_enable = data;
|
||||
}
|
||||
|
||||
static int
|
||||
uec_get_regs_len(struct net_device *netdev)
|
||||
{
|
||||
return sizeof(struct ucc_geth);
|
||||
}
|
||||
|
||||
static void
|
||||
uec_get_regs(struct net_device *netdev,
|
||||
struct ethtool_regs *regs, void *p)
|
||||
{
|
||||
int i;
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
u32 __iomem *ug_regs = (u32 __iomem *)ugeth->ug_regs;
|
||||
u32 *buff = p;
|
||||
|
||||
for (i = 0; i < sizeof(struct ucc_geth) / sizeof(u32); i++)
|
||||
buff[i] = in_be32(&ug_regs[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
uec_get_ringparam(struct net_device *netdev,
|
||||
struct ethtool_ringparam *ring)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct ucc_geth_info *ug_info = ugeth->ug_info;
|
||||
int queue = 0;
|
||||
|
||||
ring->rx_max_pending = UCC_GETH_BD_RING_SIZE_MAX;
|
||||
ring->rx_mini_max_pending = UCC_GETH_BD_RING_SIZE_MAX;
|
||||
ring->rx_jumbo_max_pending = UCC_GETH_BD_RING_SIZE_MAX;
|
||||
ring->tx_max_pending = UCC_GETH_BD_RING_SIZE_MAX;
|
||||
|
||||
ring->rx_pending = ug_info->bdRingLenRx[queue];
|
||||
ring->rx_mini_pending = ug_info->bdRingLenRx[queue];
|
||||
ring->rx_jumbo_pending = ug_info->bdRingLenRx[queue];
|
||||
ring->tx_pending = ug_info->bdRingLenTx[queue];
|
||||
}
|
||||
|
||||
static int
|
||||
uec_set_ringparam(struct net_device *netdev,
|
||||
struct ethtool_ringparam *ring)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct ucc_geth_info *ug_info = ugeth->ug_info;
|
||||
int queue = 0, ret = 0;
|
||||
|
||||
if (ring->rx_pending < UCC_GETH_RX_BD_RING_SIZE_MIN) {
|
||||
printk("%s: RxBD ring size must be no smaller than %d.\n",
|
||||
netdev->name, UCC_GETH_RX_BD_RING_SIZE_MIN);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ring->rx_pending % UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT) {
|
||||
printk("%s: RxBD ring size must be multiple of %d.\n",
|
||||
netdev->name, UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ring->tx_pending < UCC_GETH_TX_BD_RING_SIZE_MIN) {
|
||||
printk("%s: TxBD ring size must be no smaller than %d.\n",
|
||||
netdev->name, UCC_GETH_TX_BD_RING_SIZE_MIN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ug_info->bdRingLenRx[queue] = ring->rx_pending;
|
||||
ug_info->bdRingLenTx[queue] = ring->tx_pending;
|
||||
|
||||
if (netif_running(netdev)) {
|
||||
/* FIXME: restart automatically */
|
||||
printk(KERN_INFO
|
||||
"Please re-open the interface.\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uec_get_sset_count(struct net_device *netdev, int sset)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
u32 stats_mode = ugeth->ug_info->statisticsMode;
|
||||
int len = 0;
|
||||
|
||||
switch (sset) {
|
||||
case ETH_SS_STATS:
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE)
|
||||
len += UEC_HW_STATS_LEN;
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX)
|
||||
len += UEC_TX_FW_STATS_LEN;
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX)
|
||||
len += UEC_RX_FW_STATS_LEN;
|
||||
|
||||
return len;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static void uec_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
u32 stats_mode = ugeth->ug_info->statisticsMode;
|
||||
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE) {
|
||||
memcpy(buf, hw_stat_gstrings, UEC_HW_STATS_LEN *
|
||||
ETH_GSTRING_LEN);
|
||||
buf += UEC_HW_STATS_LEN * ETH_GSTRING_LEN;
|
||||
}
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX) {
|
||||
memcpy(buf, tx_fw_stat_gstrings, UEC_TX_FW_STATS_LEN *
|
||||
ETH_GSTRING_LEN);
|
||||
buf += UEC_TX_FW_STATS_LEN * ETH_GSTRING_LEN;
|
||||
}
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX)
|
||||
memcpy(buf, rx_fw_stat_gstrings, UEC_RX_FW_STATS_LEN *
|
||||
ETH_GSTRING_LEN);
|
||||
}
|
||||
|
||||
static void uec_get_ethtool_stats(struct net_device *netdev,
|
||||
struct ethtool_stats *stats, uint64_t *data)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
u32 stats_mode = ugeth->ug_info->statisticsMode;
|
||||
u32 __iomem *base;
|
||||
int i, j = 0;
|
||||
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE) {
|
||||
if (ugeth->ug_regs)
|
||||
base = (u32 __iomem *)&ugeth->ug_regs->tx64;
|
||||
else
|
||||
base = NULL;
|
||||
|
||||
for (i = 0; i < UEC_HW_STATS_LEN; i++)
|
||||
data[j++] = base ? in_be32(&base[i]) : 0;
|
||||
}
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX) {
|
||||
base = (u32 __iomem *)ugeth->p_tx_fw_statistics_pram;
|
||||
for (i = 0; i < UEC_TX_FW_STATS_LEN; i++)
|
||||
data[j++] = base ? in_be32(&base[i]) : 0;
|
||||
}
|
||||
if (stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX) {
|
||||
base = (u32 __iomem *)ugeth->p_rx_fw_statistics_pram;
|
||||
for (i = 0; i < UEC_RX_FW_STATS_LEN; i++)
|
||||
data[j++] = base ? in_be32(&base[i]) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int uec_nway_reset(struct net_device *netdev)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
|
||||
return phy_start_aneg(ugeth->phydev);
|
||||
}
|
||||
|
||||
/* Report driver information */
|
||||
static void
|
||||
uec_get_drvinfo(struct net_device *netdev,
|
||||
struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
strncpy(drvinfo->driver, DRV_NAME, 32);
|
||||
strncpy(drvinfo->version, DRV_VERSION, 32);
|
||||
strncpy(drvinfo->fw_version, "N/A", 32);
|
||||
strncpy(drvinfo->bus_info, "QUICC ENGINE", 32);
|
||||
drvinfo->eedump_len = 0;
|
||||
drvinfo->regdump_len = uec_get_regs_len(netdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static void uec_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct phy_device *phydev = ugeth->phydev;
|
||||
|
||||
if (phydev && phydev->irq)
|
||||
wol->supported |= WAKE_PHY;
|
||||
if (qe_alive_during_sleep())
|
||||
wol->supported |= WAKE_MAGIC;
|
||||
|
||||
wol->wolopts = ugeth->wol_en;
|
||||
}
|
||||
|
||||
static int uec_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
||||
{
|
||||
struct ucc_geth_private *ugeth = netdev_priv(netdev);
|
||||
struct phy_device *phydev = ugeth->phydev;
|
||||
|
||||
if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
|
||||
return -EINVAL;
|
||||
else if (wol->wolopts & WAKE_PHY && (!phydev || !phydev->irq))
|
||||
return -EINVAL;
|
||||
else if (wol->wolopts & WAKE_MAGIC && !qe_alive_during_sleep())
|
||||
return -EINVAL;
|
||||
|
||||
ugeth->wol_en = wol->wolopts;
|
||||
device_set_wakeup_enable(&netdev->dev, ugeth->wol_en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define uec_get_wol NULL
|
||||
#define uec_set_wol NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct ethtool_ops uec_ethtool_ops = {
|
||||
.get_settings = uec_get_settings,
|
||||
.set_settings = uec_set_settings,
|
||||
.get_drvinfo = uec_get_drvinfo,
|
||||
.get_regs_len = uec_get_regs_len,
|
||||
.get_regs = uec_get_regs,
|
||||
.get_msglevel = uec_get_msglevel,
|
||||
.set_msglevel = uec_set_msglevel,
|
||||
.nway_reset = uec_nway_reset,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = uec_get_ringparam,
|
||||
.set_ringparam = uec_set_ringparam,
|
||||
.get_pauseparam = uec_get_pauseparam,
|
||||
.set_pauseparam = uec_set_pauseparam,
|
||||
.get_sset_count = uec_get_sset_count,
|
||||
.get_strings = uec_get_strings,
|
||||
.get_ethtool_stats = uec_get_ethtool_stats,
|
||||
.get_wol = uec_get_wol,
|
||||
.set_wol = uec_set_wol,
|
||||
};
|
||||
|
||||
void uec_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &uec_ethtool_ops);
|
||||
}
|
Reference in New Issue
Block a user