net: emac: emac gigabit ethernet controller driver
Add support for the Qualcomm Technologies, Inc. EMAC gigabit Ethernet controller. This driver supports the following features: 1) Checksum offload. 2) Interrupt coalescing support. 3) SGMII phy. 4) phylib interface for external phy Based on original work by Niranjana Vishwanathapura <nvishwan@codeaurora.org> Gilad Avidov <gavidov@codeaurora.org> Signed-off-by: Timur Tabi <timur@codeaurora.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
04bed1434d
Коммит
b9b17debc6
|
@ -0,0 +1,111 @@
|
|||
Qualcomm Technologies EMAC Gigabit Ethernet Controller
|
||||
|
||||
This network controller consists of two devices: a MAC and an SGMII
|
||||
internal PHY. Each device is represented by a device tree node. A phandle
|
||||
connects the MAC node to its corresponding internal phy node. Another
|
||||
phandle points to the external PHY node.
|
||||
|
||||
Required properties:
|
||||
|
||||
MAC node:
|
||||
- compatible : Should be "qcom,fsm9900-emac".
|
||||
- reg : Offset and length of the register regions for the device
|
||||
- interrupts : Interrupt number used by this controller
|
||||
- mac-address : The 6-byte MAC address. If present, it is the default
|
||||
MAC address.
|
||||
- internal-phy : phandle to the internal PHY node
|
||||
- phy-handle : phandle the the external PHY node
|
||||
|
||||
Internal PHY node:
|
||||
- compatible : Should be "qcom,fsm9900-emac-sgmii" or "qcom,qdf2432-emac-sgmii".
|
||||
- reg : Offset and length of the register region(s) for the device
|
||||
- interrupts : Interrupt number used by this controller
|
||||
|
||||
The external phy child node:
|
||||
- reg : The phy address
|
||||
|
||||
Example:
|
||||
|
||||
FSM9900:
|
||||
|
||||
soc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
emac0: ethernet@feb20000 {
|
||||
compatible = "qcom,fsm9900-emac";
|
||||
reg = <0xfeb20000 0x10000>,
|
||||
<0xfeb36000 0x1000>;
|
||||
interrupts = <76>;
|
||||
|
||||
clocks = <&gcc 0>, <&gcc 1>, <&gcc 3>, <&gcc 4>, <&gcc 5>,
|
||||
<&gcc 6>, <&gcc 7>;
|
||||
clock-names = "axi_clk", "cfg_ahb_clk", "high_speed_clk",
|
||||
"mdio_clk", "tx_clk", "rx_clk", "sys_clk";
|
||||
|
||||
internal-phy = <&emac_sgmii>;
|
||||
|
||||
phy-handle = <&phy0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mdio_pins_a>;
|
||||
};
|
||||
|
||||
emac_sgmii: ethernet@feb38000 {
|
||||
compatible = "qcom,fsm9900-emac-sgmii";
|
||||
reg = <0xfeb38000 0x1000>;
|
||||
interrupts = <80>;
|
||||
};
|
||||
|
||||
tlmm: pinctrl@fd510000 {
|
||||
compatible = "qcom,fsm9900-pinctrl";
|
||||
|
||||
mdio_pins_a: mdio {
|
||||
state {
|
||||
pins = "gpio123", "gpio124";
|
||||
function = "mdio";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
QDF2432:
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
emac0: ethernet@38800000 {
|
||||
compatible = "qcom,fsm9900-emac";
|
||||
reg = <0x0 0x38800000 0x0 0x10000>,
|
||||
<0x0 0x38816000 0x0 0x1000>;
|
||||
interrupts = <0 256 4>;
|
||||
|
||||
clocks = <&gcc 0>, <&gcc 1>, <&gcc 3>, <&gcc 4>, <&gcc 5>,
|
||||
<&gcc 6>, <&gcc 7>;
|
||||
clock-names = "axi_clk", "cfg_ahb_clk", "high_speed_clk",
|
||||
"mdio_clk", "tx_clk", "rx_clk", "sys_clk";
|
||||
|
||||
internal-phy = <&emac_sgmii>;
|
||||
|
||||
phy-handle = <&phy0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy0: ethernet-phy@4 {
|
||||
reg = <4>;
|
||||
};
|
||||
};
|
||||
|
||||
emac_sgmii: ethernet@410400 {
|
||||
compatible = "qcom,qdf2432-emac-sgmii";
|
||||
reg = <0x0 0x00410400 0x0 0xc00>, /* Base address */
|
||||
<0x0 0x00410000 0x0 0x400>; /* Per-lane digital */
|
||||
interrupts = <0 254 1>;
|
||||
};
|
|
@ -9696,6 +9696,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
|
|||
S: Supported
|
||||
F: drivers/net/wireless/ath/ath10k/
|
||||
|
||||
QUALCOMM EMAC GIGABIT ETHERNET DRIVER
|
||||
M: Timur Tabi <timur@codeaurora.org>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/ethernet/qualcomm/emac/
|
||||
|
||||
QUALCOMM HEXAGON ARCHITECTURE
|
||||
M: Richard Kuo <rkuo@codeaurora.org>
|
||||
L: linux-hexagon@vger.kernel.org
|
||||
|
|
|
@ -24,4 +24,16 @@ config QCA7000
|
|||
To compile this driver as a module, choose M here. The module
|
||||
will be called qcaspi.
|
||||
|
||||
config QCOM_EMAC
|
||||
tristate "Qualcomm Technologies, Inc. EMAC Gigabit Ethernet support"
|
||||
select CRC32
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports the Qualcomm Technologies, Inc. Gigabit
|
||||
Ethernet Media Access Controller (EMAC). The controller
|
||||
supports IEEE 802.3-2002, half-duplex mode at 10/100 Mb/s,
|
||||
full-duplex mode at 10/100/1000Mb/s, Wake On LAN (WOL) for
|
||||
low power, Receive-Side Scaling (RSS), and IEEE 1588-2008
|
||||
Precision Clock Synchronization Protocol.
|
||||
|
||||
endif # NET_VENDOR_QUALCOMM
|
||||
|
|
|
@ -4,3 +4,5 @@
|
|||
|
||||
obj-$(CONFIG_QCA7000) += qcaspi.o
|
||||
qcaspi-objs := qca_spi.o qca_framing.o qca_7k.o qca_debug.o
|
||||
|
||||
obj-y += emac/
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Makefile for the Qualcomm Technologies, Inc. EMAC Gigabit Ethernet driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o
|
||||
|
||||
qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,248 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* EMAC DMA HW engine uses three rings:
|
||||
* Tx:
|
||||
* TPD: Transmit Packet Descriptor ring.
|
||||
* Rx:
|
||||
* RFD: Receive Free Descriptor ring.
|
||||
* Ring of descriptors with empty buffers to be filled by Rx HW.
|
||||
* RRD: Receive Return Descriptor ring.
|
||||
* Ring of descriptors with buffers filled with received data.
|
||||
*/
|
||||
|
||||
#ifndef _EMAC_HW_H_
|
||||
#define _EMAC_HW_H_
|
||||
|
||||
/* EMAC_CSR register offsets */
|
||||
#define EMAC_EMAC_WRAPPER_CSR1 0x000000
|
||||
#define EMAC_EMAC_WRAPPER_CSR2 0x000004
|
||||
#define EMAC_EMAC_WRAPPER_TX_TS_LO 0x000104
|
||||
#define EMAC_EMAC_WRAPPER_TX_TS_HI 0x000108
|
||||
#define EMAC_EMAC_WRAPPER_TX_TS_INX 0x00010c
|
||||
|
||||
/* DMA Order Settings */
|
||||
enum emac_dma_order {
|
||||
emac_dma_ord_in = 1,
|
||||
emac_dma_ord_enh = 2,
|
||||
emac_dma_ord_out = 4
|
||||
};
|
||||
|
||||
enum emac_dma_req_block {
|
||||
emac_dma_req_128 = 0,
|
||||
emac_dma_req_256 = 1,
|
||||
emac_dma_req_512 = 2,
|
||||
emac_dma_req_1024 = 3,
|
||||
emac_dma_req_2048 = 4,
|
||||
emac_dma_req_4096 = 5
|
||||
};
|
||||
|
||||
/* Returns the value of bits idx...idx+n_bits */
|
||||
#define BITS_GET(val, lo, hi) ((le32_to_cpu(val) & GENMASK((hi), (lo))) >> lo)
|
||||
#define BITS_SET(val, lo, hi, new_val) \
|
||||
val = cpu_to_le32((le32_to_cpu(val) & (~GENMASK((hi), (lo)))) | \
|
||||
(((new_val) << (lo)) & GENMASK((hi), (lo))))
|
||||
|
||||
/* RRD (Receive Return Descriptor) */
|
||||
struct emac_rrd {
|
||||
u32 word[6];
|
||||
|
||||
/* number of RFD */
|
||||
#define RRD_NOR(rrd) BITS_GET((rrd)->word[0], 16, 19)
|
||||
/* start consumer index of rfd-ring */
|
||||
#define RRD_SI(rrd) BITS_GET((rrd)->word[0], 20, 31)
|
||||
/* vlan-tag (CVID, CFI and PRI) */
|
||||
#define RRD_CVALN_TAG(rrd) BITS_GET((rrd)->word[2], 0, 15)
|
||||
/* length of the packet */
|
||||
#define RRD_PKT_SIZE(rrd) BITS_GET((rrd)->word[3], 0, 13)
|
||||
/* L4(TCP/UDP) checksum failed */
|
||||
#define RRD_L4F(rrd) BITS_GET((rrd)->word[3], 14, 14)
|
||||
/* vlan tagged */
|
||||
#define RRD_CVTAG(rrd) BITS_GET((rrd)->word[3], 16, 16)
|
||||
/* When set, indicates that the descriptor is updated by the IP core.
|
||||
* When cleared, indicates that the descriptor is invalid.
|
||||
*/
|
||||
#define RRD_UPDT(rrd) BITS_GET((rrd)->word[3], 31, 31)
|
||||
#define RRD_UPDT_SET(rrd, val) BITS_SET((rrd)->word[3], 31, 31, val)
|
||||
/* timestamp low */
|
||||
#define RRD_TS_LOW(rrd) BITS_GET((rrd)->word[4], 0, 29)
|
||||
/* timestamp high */
|
||||
#define RRD_TS_HI(rrd) le32_to_cpu((rrd)->word[5])
|
||||
};
|
||||
|
||||
/* TPD (Transmit Packet Descriptor) */
|
||||
struct emac_tpd {
|
||||
u32 word[4];
|
||||
|
||||
/* Number of bytes of the transmit packet. (include 4-byte CRC) */
|
||||
#define TPD_BUF_LEN_SET(tpd, val) BITS_SET((tpd)->word[0], 0, 15, val)
|
||||
/* Custom Checksum Offload: When set, ask IP core to offload custom checksum */
|
||||
#define TPD_CSX_SET(tpd, val) BITS_SET((tpd)->word[1], 8, 8, val)
|
||||
/* TCP Large Send Offload: When set, ask IP core to do offload TCP Large Send */
|
||||
#define TPD_LSO(tpd) BITS_GET((tpd)->word[1], 12, 12)
|
||||
#define TPD_LSO_SET(tpd, val) BITS_SET((tpd)->word[1], 12, 12, val)
|
||||
/* Large Send Offload Version: When set, indicates this is an LSOv2
|
||||
* (for both IPv4 and IPv6). When cleared, indicates this is an LSOv1
|
||||
* (only for IPv4).
|
||||
*/
|
||||
#define TPD_LSOV_SET(tpd, val) BITS_SET((tpd)->word[1], 13, 13, val)
|
||||
/* IPv4 packet: When set, indicates this is an IPv4 packet, this bit is only
|
||||
* for LSOV2 format.
|
||||
*/
|
||||
#define TPD_IPV4_SET(tpd, val) BITS_SET((tpd)->word[1], 16, 16, val)
|
||||
/* 0: Ethernet frame (DA+SA+TYPE+DATA+CRC)
|
||||
* 1: IEEE 802.3 frame (DA+SA+LEN+DSAP+SSAP+CTL+ORG+TYPE+DATA+CRC)
|
||||
*/
|
||||
#define TPD_TYP_SET(tpd, val) BITS_SET((tpd)->word[1], 17, 17, val)
|
||||
/* Low-32bit Buffer Address */
|
||||
#define TPD_BUFFER_ADDR_L_SET(tpd, val) ((tpd)->word[2] = cpu_to_le32(val))
|
||||
/* CVLAN Tag to be inserted if INS_VLAN_TAG is set, CVLAN TPID based on global
|
||||
* register configuration.
|
||||
*/
|
||||
#define TPD_CVLAN_TAG_SET(tpd, val) BITS_SET((tpd)->word[3], 0, 15, val)
|
||||
/* Insert CVlan Tag: When set, ask MAC to insert CVLAN TAG to outgoing packet
|
||||
*/
|
||||
#define TPD_INSTC_SET(tpd, val) BITS_SET((tpd)->word[3], 17, 17, val)
|
||||
/* High-14bit Buffer Address, So, the 64b-bit address is
|
||||
* {DESC_CTRL_11_TX_DATA_HIADDR[17:0],(register) BUFFER_ADDR_H, BUFFER_ADDR_L}
|
||||
*/
|
||||
#define TPD_BUFFER_ADDR_H_SET(tpd, val) BITS_SET((tpd)->word[3], 18, 30, val)
|
||||
/* Format D. Word offset from the 1st byte of this packet to start to calculate
|
||||
* the custom checksum.
|
||||
*/
|
||||
#define TPD_PAYLOAD_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 0, 7, val)
|
||||
/* Format D. Word offset from the 1st byte of this packet to fill the custom
|
||||
* checksum to
|
||||
*/
|
||||
#define TPD_CXSUM_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 18, 25, val)
|
||||
|
||||
/* Format C. TCP Header offset from the 1st byte of this packet. (byte unit) */
|
||||
#define TPD_TCPHDR_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 0, 7, val)
|
||||
/* Format C. MSS (Maximum Segment Size) got from the protocol layer. (byte unit)
|
||||
*/
|
||||
#define TPD_MSS_SET(tpd, val) BITS_SET((tpd)->word[1], 18, 30, val)
|
||||
/* packet length in ext tpd */
|
||||
#define TPD_PKT_LEN_SET(tpd, val) ((tpd)->word[2] = cpu_to_le32(val))
|
||||
};
|
||||
|
||||
/* emac_ring_header represents a single, contiguous block of DMA space
|
||||
* mapped for the three descriptor rings (tpd, rfd, rrd)
|
||||
*/
|
||||
struct emac_ring_header {
|
||||
void *v_addr; /* virtual address */
|
||||
dma_addr_t dma_addr; /* dma address */
|
||||
size_t size; /* length in bytes */
|
||||
size_t used;
|
||||
};
|
||||
|
||||
/* emac_buffer is wrapper around a pointer to a socket buffer
|
||||
* so a DMA handle can be stored along with the skb
|
||||
*/
|
||||
struct emac_buffer {
|
||||
struct sk_buff *skb; /* socket buffer */
|
||||
u16 length; /* rx buffer length */
|
||||
dma_addr_t dma_addr; /* dma address */
|
||||
};
|
||||
|
||||
/* receive free descriptor (rfd) ring */
|
||||
struct emac_rfd_ring {
|
||||
struct emac_buffer *rfbuff;
|
||||
u32 *v_addr; /* virtual address */
|
||||
dma_addr_t dma_addr; /* dma address */
|
||||
size_t size; /* length in bytes */
|
||||
unsigned int count; /* number of desc in the ring */
|
||||
unsigned int produce_idx;
|
||||
unsigned int process_idx;
|
||||
unsigned int consume_idx; /* unused */
|
||||
};
|
||||
|
||||
/* Receive Return Desciptor (RRD) ring */
|
||||
struct emac_rrd_ring {
|
||||
u32 *v_addr; /* virtual address */
|
||||
dma_addr_t dma_addr; /* physical address */
|
||||
size_t size; /* length in bytes */
|
||||
unsigned int count; /* number of desc in the ring */
|
||||
unsigned int produce_idx; /* unused */
|
||||
unsigned int consume_idx;
|
||||
};
|
||||
|
||||
/* Rx queue */
|
||||
struct emac_rx_queue {
|
||||
struct net_device *netdev; /* netdev ring belongs to */
|
||||
struct emac_rrd_ring rrd;
|
||||
struct emac_rfd_ring rfd;
|
||||
struct napi_struct napi;
|
||||
struct emac_irq *irq;
|
||||
|
||||
u32 intr;
|
||||
u32 produce_mask;
|
||||
u32 process_mask;
|
||||
u32 consume_mask;
|
||||
|
||||
u16 produce_reg;
|
||||
u16 process_reg;
|
||||
u16 consume_reg;
|
||||
|
||||
u8 produce_shift;
|
||||
u8 process_shft;
|
||||
u8 consume_shift;
|
||||
};
|
||||
|
||||
/* Transimit Packet Descriptor (tpd) ring */
|
||||
struct emac_tpd_ring {
|
||||
struct emac_buffer *tpbuff;
|
||||
u32 *v_addr; /* virtual address */
|
||||
dma_addr_t dma_addr; /* dma address */
|
||||
|
||||
size_t size; /* length in bytes */
|
||||
unsigned int count; /* number of desc in the ring */
|
||||
unsigned int produce_idx;
|
||||
unsigned int consume_idx;
|
||||
unsigned int last_produce_idx;
|
||||
};
|
||||
|
||||
/* Tx queue */
|
||||
struct emac_tx_queue {
|
||||
struct emac_tpd_ring tpd;
|
||||
|
||||
u32 produce_mask;
|
||||
u32 consume_mask;
|
||||
|
||||
u16 max_packets; /* max packets per interrupt */
|
||||
u16 produce_reg;
|
||||
u16 consume_reg;
|
||||
|
||||
u8 produce_shift;
|
||||
u8 consume_shift;
|
||||
};
|
||||
|
||||
struct emac_adapter;
|
||||
|
||||
int emac_mac_up(struct emac_adapter *adpt);
|
||||
void emac_mac_down(struct emac_adapter *adpt);
|
||||
void emac_mac_reset(struct emac_adapter *adpt);
|
||||
void emac_mac_start(struct emac_adapter *adpt);
|
||||
void emac_mac_stop(struct emac_adapter *adpt);
|
||||
void emac_mac_mode_config(struct emac_adapter *adpt);
|
||||
void emac_mac_rx_process(struct emac_adapter *adpt, struct emac_rx_queue *rx_q,
|
||||
int *num_pkts, int max_pkts);
|
||||
int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q,
|
||||
struct sk_buff *skb);
|
||||
void emac_mac_tx_process(struct emac_adapter *adpt, struct emac_tx_queue *tx_q);
|
||||
void emac_mac_rx_tx_ring_init_all(struct platform_device *pdev,
|
||||
struct emac_adapter *adpt);
|
||||
int emac_mac_rx_tx_rings_alloc_all(struct emac_adapter *adpt);
|
||||
void emac_mac_rx_tx_rings_free_all(struct emac_adapter *adpt);
|
||||
void emac_mac_multicast_addr_clear(struct emac_adapter *adpt);
|
||||
void emac_mac_multicast_addr_set(struct emac_adapter *adpt, u8 *addr);
|
||||
|
||||
#endif /*_EMAC_HW_H_*/
|
|
@ -0,0 +1,204 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Qualcomm Technologies, Inc. EMAC PHY Controller driver.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include "emac.h"
|
||||
#include "emac-mac.h"
|
||||
#include "emac-phy.h"
|
||||
#include "emac-sgmii.h"
|
||||
|
||||
/* EMAC base register offsets */
|
||||
#define EMAC_MDIO_CTRL 0x001414
|
||||
#define EMAC_PHY_STS 0x001418
|
||||
#define EMAC_MDIO_EX_CTRL 0x001440
|
||||
|
||||
/* EMAC_MDIO_CTRL */
|
||||
#define MDIO_MODE BIT(30)
|
||||
#define MDIO_PR BIT(29)
|
||||
#define MDIO_AP_EN BIT(28)
|
||||
#define MDIO_BUSY BIT(27)
|
||||
#define MDIO_CLK_SEL_BMSK 0x7000000
|
||||
#define MDIO_CLK_SEL_SHFT 24
|
||||
#define MDIO_START BIT(23)
|
||||
#define SUP_PREAMBLE BIT(22)
|
||||
#define MDIO_RD_NWR BIT(21)
|
||||
#define MDIO_REG_ADDR_BMSK 0x1f0000
|
||||
#define MDIO_REG_ADDR_SHFT 16
|
||||
#define MDIO_DATA_BMSK 0xffff
|
||||
#define MDIO_DATA_SHFT 0
|
||||
|
||||
/* EMAC_PHY_STS */
|
||||
#define PHY_ADDR_BMSK 0x1f0000
|
||||
#define PHY_ADDR_SHFT 16
|
||||
|
||||
#define MDIO_CLK_25_4 0
|
||||
#define MDIO_CLK_25_28 7
|
||||
|
||||
#define MDIO_WAIT_TIMES 1000
|
||||
|
||||
#define EMAC_LINK_SPEED_DEFAULT (\
|
||||
EMAC_LINK_SPEED_10_HALF |\
|
||||
EMAC_LINK_SPEED_10_FULL |\
|
||||
EMAC_LINK_SPEED_100_HALF |\
|
||||
EMAC_LINK_SPEED_100_FULL |\
|
||||
EMAC_LINK_SPEED_1GB_FULL)
|
||||
|
||||
/**
|
||||
* emac_phy_mdio_autopoll_disable() - disable mdio autopoll
|
||||
* @adpt: the emac adapter
|
||||
*
|
||||
* The autopoll feature takes over the MDIO bus. In order for
|
||||
* the PHY driver to be able to talk to the PHY over the MDIO
|
||||
* bus, we need to temporarily disable the autopoll feature.
|
||||
*/
|
||||
static int emac_phy_mdio_autopoll_disable(struct emac_adapter *adpt)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* disable autopoll */
|
||||
emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, MDIO_AP_EN, 0);
|
||||
|
||||
/* wait for any mdio polling to complete */
|
||||
if (!readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, val,
|
||||
!(val & MDIO_BUSY), 100, MDIO_WAIT_TIMES * 100))
|
||||
return 0;
|
||||
|
||||
/* failed to disable; ensure it is enabled before returning */
|
||||
emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/**
|
||||
* emac_phy_mdio_autopoll_disable() - disable mdio autopoll
|
||||
* @adpt: the emac adapter
|
||||
*
|
||||
* The EMAC has the ability to poll the external PHY on the MDIO
|
||||
* bus for link state changes. This eliminates the need for the
|
||||
* driver to poll the phy. If if the link state does change,
|
||||
* the EMAC issues an interrupt on behalf of the PHY.
|
||||
*/
|
||||
static void emac_phy_mdio_autopoll_enable(struct emac_adapter *adpt)
|
||||
{
|
||||
emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, 0, MDIO_AP_EN);
|
||||
}
|
||||
|
||||
static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum)
|
||||
{
|
||||
struct emac_adapter *adpt = bus->priv;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
ret = emac_phy_mdio_autopoll_disable(adpt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK,
|
||||
(addr << PHY_ADDR_SHFT));
|
||||
|
||||
reg = SUP_PREAMBLE |
|
||||
((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
|
||||
((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
|
||||
MDIO_START | MDIO_RD_NWR;
|
||||
|
||||
writel(reg, adpt->base + EMAC_MDIO_CTRL);
|
||||
|
||||
if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg,
|
||||
!(reg & (MDIO_START | MDIO_BUSY)),
|
||||
100, MDIO_WAIT_TIMES * 100))
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK;
|
||||
|
||||
emac_phy_mdio_autopoll_enable(adpt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
|
||||
{
|
||||
struct emac_adapter *adpt = bus->priv;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
ret = emac_phy_mdio_autopoll_disable(adpt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK,
|
||||
(addr << PHY_ADDR_SHFT));
|
||||
|
||||
reg = SUP_PREAMBLE |
|
||||
((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) |
|
||||
((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) |
|
||||
((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) |
|
||||
MDIO_START;
|
||||
|
||||
writel(reg, adpt->base + EMAC_MDIO_CTRL);
|
||||
|
||||
if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg,
|
||||
!(reg & (MDIO_START | MDIO_BUSY)), 100,
|
||||
MDIO_WAIT_TIMES * 100))
|
||||
ret = -EIO;
|
||||
|
||||
emac_phy_mdio_autopoll_enable(adpt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure the MDIO bus and connect the external PHY */
|
||||
int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *phy_np;
|
||||
struct mii_bus *mii_bus;
|
||||
int ret;
|
||||
|
||||
/* Create the mii_bus object for talking to the MDIO bus */
|
||||
adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev);
|
||||
if (!mii_bus)
|
||||
return -ENOMEM;
|
||||
|
||||
mii_bus->name = "emac-mdio";
|
||||
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
|
||||
mii_bus->read = emac_mdio_read;
|
||||
mii_bus->write = emac_mdio_write;
|
||||
mii_bus->parent = &pdev->dev;
|
||||
mii_bus->priv = adpt;
|
||||
|
||||
ret = of_mdiobus_register(mii_bus, np);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register mdio bus\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_np = of_parse_phandle(np, "phy-handle", 0);
|
||||
adpt->phydev = of_phy_find_device(phy_np);
|
||||
if (!adpt->phydev) {
|
||||
dev_err(&pdev->dev, "could not find external phy\n");
|
||||
mdiobus_unregister(mii_bus);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (adpt->phydev->drv)
|
||||
phy_attached_print(adpt->phydev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EMAC_PHY_H_
|
||||
#define _EMAC_PHY_H_
|
||||
|
||||
typedef int (*emac_sgmii_initialize)(struct emac_adapter *adpt);
|
||||
|
||||
/** emac_phy - internal emac phy
|
||||
* @base base address
|
||||
* @digital per-lane digital block
|
||||
* @initialize initialization function
|
||||
*/
|
||||
struct emac_phy {
|
||||
void __iomem *base;
|
||||
void __iomem *digital;
|
||||
emac_sgmii_initialize initialize;
|
||||
};
|
||||
|
||||
struct emac_adapter;
|
||||
|
||||
int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt);
|
||||
|
||||
#endif /* _EMAC_PHY_H_ */
|
|
@ -0,0 +1,721 @@
|
|||
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Qualcomm Technologies, Inc. EMAC SGMII Controller driver.
|
||||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "emac.h"
|
||||
#include "emac-mac.h"
|
||||
#include "emac-sgmii.h"
|
||||
|
||||
/* EMAC_QSERDES register offsets */
|
||||
#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x000000
|
||||
#define EMAC_QSERDES_COM_PLL_CNTRL 0x000014
|
||||
#define EMAC_QSERDES_COM_PLL_IP_SETI 0x000018
|
||||
#define EMAC_QSERDES_COM_PLL_CP_SETI 0x000024
|
||||
#define EMAC_QSERDES_COM_PLL_IP_SETP 0x000028
|
||||
#define EMAC_QSERDES_COM_PLL_CP_SETP 0x00002c
|
||||
#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x000038
|
||||
#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x000040
|
||||
#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x000044
|
||||
#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x000048
|
||||
#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x00004c
|
||||
#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x000050
|
||||
#define EMAC_QSERDES_COM_DEC_START1 0x000064
|
||||
#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x000098
|
||||
#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x00009c
|
||||
#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x0000a0
|
||||
#define EMAC_QSERDES_COM_DEC_START2 0x0000a4
|
||||
#define EMAC_QSERDES_COM_PLL_CRCTRL 0x0000ac
|
||||
#define EMAC_QSERDES_COM_RESET_SM 0x0000bc
|
||||
#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x000100
|
||||
#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x000108
|
||||
#define EMAC_QSERDES_TX_TX_DRV_LVL 0x00010c
|
||||
#define EMAC_QSERDES_TX_LANE_MODE 0x000150
|
||||
#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x000170
|
||||
#define EMAC_QSERDES_RX_CDR_CONTROL 0x000200
|
||||
#define EMAC_QSERDES_RX_CDR_CONTROL2 0x000210
|
||||
#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x000230
|
||||
|
||||
/* EMAC_SGMII register offsets */
|
||||
#define EMAC_SGMII_PHY_SERDES_START 0x000000
|
||||
#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x000004
|
||||
#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x000008
|
||||
#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x00000C
|
||||
#define EMAC_SGMII_PHY_LANE_CTRL1 0x000018
|
||||
#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x000048
|
||||
#define EMAC_SGMII_PHY_CDR_CTRL0 0x000058
|
||||
#define EMAC_SGMII_PHY_SPEED_CFG1 0x000074
|
||||
#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x000080
|
||||
#define EMAC_SGMII_PHY_RESET_CTRL 0x0000a8
|
||||
#define EMAC_SGMII_PHY_IRQ_CMD 0x0000ac
|
||||
#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x0000b0
|
||||
#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x0000b4
|
||||
#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x0000b8
|
||||
#define EMAC_SGMII_PHY_RX_CHK_STATUS 0x0000d4
|
||||
#define EMAC_SGMII_PHY_AUTONEG0_STATUS 0x0000e0
|
||||
#define EMAC_SGMII_PHY_AUTONEG1_STATUS 0x0000e4
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_IP_SETI */
|
||||
#define PLL_IPSETI(x) ((x) & 0x3f)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_CP_SETI */
|
||||
#define PLL_CPSETI(x) ((x) & 0xff)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_IP_SETP */
|
||||
#define PLL_IPSETP(x) ((x) & 0x3f)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_CP_SETP */
|
||||
#define PLL_CPSETP(x) ((x) & 0x1f)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_CRCTRL */
|
||||
#define PLL_RCTRL(x) (((x) & 0xf) << 4)
|
||||
#define PLL_CCTRL(x) ((x) & 0xf)
|
||||
|
||||
/* SGMII v2 PHY registers per lane */
|
||||
#define EMAC_SGMII_PHY_LN_OFFSET 0x0400
|
||||
|
||||
/* SGMII v2 digital lane registers */
|
||||
#define EMAC_SGMII_LN_DRVR_CTRL0 0x00C
|
||||
#define EMAC_SGMII_LN_DRVR_TAP_EN 0x018
|
||||
#define EMAC_SGMII_LN_TX_MARGINING 0x01C
|
||||
#define EMAC_SGMII_LN_TX_PRE 0x020
|
||||
#define EMAC_SGMII_LN_TX_POST 0x024
|
||||
#define EMAC_SGMII_LN_TX_BAND_MODE 0x060
|
||||
#define EMAC_SGMII_LN_LANE_MODE 0x064
|
||||
#define EMAC_SGMII_LN_PARALLEL_RATE 0x078
|
||||
#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x0B8
|
||||
#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x0D0
|
||||
#define EMAC_SGMII_LN_VGA_INITVAL 0x134
|
||||
#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x17C
|
||||
#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x188
|
||||
#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x194
|
||||
#define EMAC_SGMII_LN_RX_BAND 0x19C
|
||||
#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x1B8
|
||||
#define EMAC_SGMII_LN_RSM_CONFIG 0x1F0
|
||||
#define EMAC_SGMII_LN_SIGDET_ENABLES 0x224
|
||||
#define EMAC_SGMII_LN_SIGDET_CNTRL 0x228
|
||||
#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x22C
|
||||
#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x2A0
|
||||
#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x2AC
|
||||
#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x2BC
|
||||
|
||||
/* SGMII v2 digital lane register values */
|
||||
#define UCDR_STEP_BY_TWO_MODE0 BIT(7)
|
||||
#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f)
|
||||
#define UCDR_ENABLE BIT(6)
|
||||
#define UCDR_SO_SATURATION(x) ((x) & 0x3f)
|
||||
#define SIGDET_LP_BYP_PS4 BIT(7)
|
||||
#define SIGDET_EN_PS0_TO_PS2 BIT(6)
|
||||
#define EN_ACCOUPLEVCM_SW_MUX BIT(5)
|
||||
#define EN_ACCOUPLEVCM_SW BIT(4)
|
||||
#define RX_SYNC_EN BIT(3)
|
||||
#define RXTERM_HIGHZ_PS5 BIT(2)
|
||||
#define SIGDET_EN_PS3 BIT(1)
|
||||
#define EN_ACCOUPLE_VCM_PS3 BIT(0)
|
||||
#define UFS_MODE BIT(5)
|
||||
#define TXVAL_VALID_INIT BIT(4)
|
||||
#define TXVAL_VALID_MUX BIT(3)
|
||||
#define TXVAL_VALID BIT(2)
|
||||
#define USB3P1_MODE BIT(1)
|
||||
#define KR_PCIGEN3_MODE BIT(0)
|
||||
#define PRE_EN BIT(3)
|
||||
#define POST_EN BIT(2)
|
||||
#define MAIN_EN_MUX BIT(1)
|
||||
#define MAIN_EN BIT(0)
|
||||
#define TX_MARGINING_MUX BIT(6)
|
||||
#define TX_MARGINING(x) ((x) & 0x3f)
|
||||
#define TX_PRE_MUX BIT(6)
|
||||
#define TX_PRE(x) ((x) & 0x3f)
|
||||
#define TX_POST_MUX BIT(6)
|
||||
#define TX_POST(x) ((x) & 0x3f)
|
||||
#define CML_GEAR_MODE(x) (((x) & 7) << 3)
|
||||
#define CML2CMOS_IBOOST_MODE(x) ((x) & 7)
|
||||
#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2)
|
||||
#define MIXER_DATARATE_MODE(x) ((x) & 3)
|
||||
#define VGA_THRESH_DFE(x) ((x) & 0x3f)
|
||||
#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5)
|
||||
#define SIGDET_LP_BYP_MUX BIT(4)
|
||||
#define SIGDET_LP_BYP BIT(3)
|
||||
#define SIGDET_EN_MUX BIT(2)
|
||||
#define SIGDET_EN BIT(1)
|
||||
#define SIGDET_FLT_BYP BIT(0)
|
||||
#define SIGDET_LVL(x) (((x) & 0xf) << 4)
|
||||
#define SIGDET_BW_CTRL(x) ((x) & 0xf)
|
||||
#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1)
|
||||
#define SIGDET_DEGLITCH_BYP BIT(0)
|
||||
#define INVERT_PCS_RX_CLK BIT(7)
|
||||
#define PWM_EN BIT(6)
|
||||
#define RXBIAS_SEL(x) (((x) & 0x3) << 4)
|
||||
#define EBDAC_SIGN BIT(3)
|
||||
#define EDAC_SIGN BIT(2)
|
||||
#define EN_AUXTAP1SIGN_INVERT BIT(1)
|
||||
#define EN_DAC_CHOPPING BIT(0)
|
||||
#define DRVR_LOGIC_CLK_EN BIT(4)
|
||||
#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf)
|
||||
#define PARALLEL_RATE_MODE2(x) (((x) & 0x3) << 4)
|
||||
#define PARALLEL_RATE_MODE1(x) (((x) & 0x3) << 2)
|
||||
#define PARALLEL_RATE_MODE0(x) ((x) & 0x3)
|
||||
#define BAND_MODE2(x) (((x) & 0x3) << 4)
|
||||
#define BAND_MODE1(x) (((x) & 0x3) << 2)
|
||||
#define BAND_MODE0(x) ((x) & 0x3)
|
||||
#define LANE_SYNC_MODE BIT(5)
|
||||
#define LANE_MODE(x) ((x) & 0x1f)
|
||||
#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5)
|
||||
#define EN_DLL_MODE0 BIT(4)
|
||||
#define EN_IQ_DCC_MODE0 BIT(3)
|
||||
#define EN_IQCAL_MODE0 BIT(2)
|
||||
#define EN_QPATH_MODE0 BIT(1)
|
||||
#define EN_EPATH_MODE0 BIT(0)
|
||||
#define FORCE_TSYNC_ACK BIT(7)
|
||||
#define FORCE_CMN_ACK BIT(6)
|
||||
#define FORCE_CMN_READY BIT(5)
|
||||
#define EN_RCLK_DEGLITCH BIT(4)
|
||||
#define BYPASS_RSM_CDR_RESET BIT(3)
|
||||
#define BYPASS_RSM_TSYNC BIT(2)
|
||||
#define BYPASS_RSM_SAMP_CAL BIT(1)
|
||||
#define BYPASS_RSM_DLL_CAL BIT(0)
|
||||
|
||||
/* EMAC_QSERDES_COM_SYS_CLK_CTRL */
|
||||
#define SYSCLK_CM BIT(4)
|
||||
#define SYSCLK_AC_COUPLE BIT(3)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLL_CNTRL */
|
||||
#define OCP_EN BIT(5)
|
||||
#define PLL_DIV_FFEN BIT(2)
|
||||
#define PLL_DIV_ORD BIT(1)
|
||||
|
||||
/* EMAC_QSERDES_COM_SYSCLK_EN_SEL */
|
||||
#define SYSCLK_SEL_CMOS BIT(3)
|
||||
|
||||
/* EMAC_QSERDES_COM_RESETSM_CNTRL */
|
||||
#define FRQ_TUNE_MODE BIT(4)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLLLOCK_CMP_EN */
|
||||
#define PLLLOCK_CMP_EN BIT(0)
|
||||
|
||||
/* EMAC_QSERDES_COM_DEC_START1 */
|
||||
#define DEC_START1_MUX BIT(7)
|
||||
#define DEC_START1(x) ((x) & 0x7f)
|
||||
|
||||
/* EMAC_QSERDES_COM_DIV_FRAC_START1 * EMAC_QSERDES_COM_DIV_FRAC_START2 */
|
||||
#define DIV_FRAC_START_MUX BIT(7)
|
||||
#define DIV_FRAC_START(x) ((x) & 0x7f)
|
||||
|
||||
/* EMAC_QSERDES_COM_DIV_FRAC_START3 */
|
||||
#define DIV_FRAC_START3_MUX BIT(4)
|
||||
#define DIV_FRAC_START3(x) ((x) & 0xf)
|
||||
|
||||
/* EMAC_QSERDES_COM_DEC_START2 */
|
||||
#define DEC_START2_MUX BIT(1)
|
||||
#define DEC_START2 BIT(0)
|
||||
|
||||
/* EMAC_QSERDES_COM_RESET_SM */
|
||||
#define READY BIT(5)
|
||||
|
||||
/* EMAC_QSERDES_TX_TX_EMP_POST1_LVL */
|
||||
#define TX_EMP_POST1_LVL_MUX BIT(5)
|
||||
#define TX_EMP_POST1_LVL(x) ((x) & 0x1f)
|
||||
#define TX_EMP_POST1_LVL_BMSK 0x1f
|
||||
#define TX_EMP_POST1_LVL_SHFT 0
|
||||
|
||||
/* EMAC_QSERDES_TX_TX_DRV_LVL */
|
||||
#define TX_DRV_LVL_MUX BIT(4)
|
||||
#define TX_DRV_LVL(x) ((x) & 0xf)
|
||||
|
||||
/* EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN */
|
||||
#define EMP_EN_MUX BIT(1)
|
||||
#define EMP_EN BIT(0)
|
||||
|
||||
/* EMAC_QSERDES_RX_CDR_CONTROL & EMAC_QSERDES_RX_CDR_CONTROL2 */
|
||||
#define HBW_PD_EN BIT(7)
|
||||
#define SECONDORDERENABLE BIT(6)
|
||||
#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3)
|
||||
#define SECONDORDERGAIN(x) ((x) & 0x7)
|
||||
|
||||
/* EMAC_QSERDES_RX_RX_EQ_GAIN12 */
|
||||
#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4)
|
||||
#define RX_EQ_GAIN1(x) ((x) & 0xf)
|
||||
|
||||
/* EMAC_SGMII_PHY_SERDES_START */
|
||||
#define SERDES_START BIT(0)
|
||||
|
||||
/* EMAC_SGMII_PHY_CMN_PWR_CTRL */
|
||||
#define BIAS_EN BIT(6)
|
||||
#define PLL_EN BIT(5)
|
||||
#define SYSCLK_EN BIT(4)
|
||||
#define CLKBUF_L_EN BIT(3)
|
||||
#define PLL_TXCLK_EN BIT(1)
|
||||
#define PLL_RXCLK_EN BIT(0)
|
||||
|
||||
/* EMAC_SGMII_PHY_RX_PWR_CTRL */
|
||||
#define L0_RX_SIGDET_EN BIT(7)
|
||||
#define L0_RX_TERM_MODE(x) (((x) & 3) << 4)
|
||||
#define L0_RX_I_EN BIT(1)
|
||||
|
||||
/* EMAC_SGMII_PHY_TX_PWR_CTRL */
|
||||
#define L0_TX_EN BIT(5)
|
||||
#define L0_CLKBUF_EN BIT(4)
|
||||
#define L0_TRAN_BIAS_EN BIT(1)
|
||||
|
||||
/* EMAC_SGMII_PHY_LANE_CTRL1 */
|
||||
#define L0_RX_EQUALIZE_ENABLE BIT(6)
|
||||
#define L0_RESET_TSYNC_EN BIT(4)
|
||||
#define L0_DRV_LVL(x) ((x) & 0xf)
|
||||
|
||||
/* EMAC_SGMII_PHY_AUTONEG_CFG2 */
|
||||
#define FORCE_AN_TX_CFG BIT(5)
|
||||
#define FORCE_AN_RX_CFG BIT(4)
|
||||
#define AN_ENABLE BIT(0)
|
||||
|
||||
/* EMAC_SGMII_PHY_SPEED_CFG1 */
|
||||
#define DUPLEX_MODE BIT(4)
|
||||
#define SPDMODE_1000 BIT(1)
|
||||
#define SPDMODE_100 BIT(0)
|
||||
#define SPDMODE_10 0
|
||||
#define SPDMODE_BMSK 3
|
||||
#define SPDMODE_SHFT 0
|
||||
|
||||
/* EMAC_SGMII_PHY_POW_DWN_CTRL0 */
|
||||
#define PWRDN_B BIT(0)
|
||||
#define CDR_MAX_CNT(x) ((x) & 0xff)
|
||||
|
||||
/* EMAC_QSERDES_TX_BIST_MODE_LANENO */
|
||||
#define BIST_LANE_NUMBER(x) (((x) & 3) << 5)
|
||||
#define BISTMODE(x) ((x) & 0x1f)
|
||||
|
||||
/* EMAC_QSERDES_COM_PLLLOCK_CMPx */
|
||||
#define PLLLOCK_CMP(x) ((x) & 0xff)
|
||||
|
||||
/* EMAC_SGMII_PHY_RESET_CTRL */
|
||||
#define PHY_SW_RESET BIT(0)
|
||||
|
||||
/* EMAC_SGMII_PHY_IRQ_CMD */
|
||||
#define IRQ_GLOBAL_CLEAR BIT(0)
|
||||
|
||||
/* EMAC_SGMII_PHY_INTERRUPT_MASK */
|
||||
#define DECODE_CODE_ERR BIT(7)
|
||||
#define DECODE_DISP_ERR BIT(6)
|
||||
#define PLL_UNLOCK BIT(5)
|
||||
#define AN_ILLEGAL_TERM BIT(4)
|
||||
#define SYNC_FAIL BIT(3)
|
||||
#define AN_START BIT(2)
|
||||
#define AN_END BIT(1)
|
||||
#define AN_REQUEST BIT(0)
|
||||
|
||||
#define SGMII_PHY_IRQ_CLR_WAIT_TIME 10
|
||||
|
||||
#define SGMII_PHY_INTERRUPT_ERR (\
|
||||
DECODE_CODE_ERR |\
|
||||
DECODE_DISP_ERR)
|
||||
|
||||
#define SGMII_ISR_AN_MASK (\
|
||||
AN_REQUEST |\
|
||||
AN_START |\
|
||||
AN_END |\
|
||||
AN_ILLEGAL_TERM |\
|
||||
PLL_UNLOCK |\
|
||||
SYNC_FAIL)
|
||||
|
||||
#define SGMII_ISR_MASK (\
|
||||
SGMII_PHY_INTERRUPT_ERR |\
|
||||
SGMII_ISR_AN_MASK)
|
||||
|
||||
/* SGMII TX_CONFIG */
|
||||
#define TXCFG_LINK 0x8000
|
||||
#define TXCFG_MODE_BMSK 0x1c00
|
||||
#define TXCFG_1000_FULL 0x1800
|
||||
#define TXCFG_100_FULL 0x1400
|
||||
#define TXCFG_100_HALF 0x0400
|
||||
#define TXCFG_10_FULL 0x1000
|
||||
#define TXCFG_10_HALF 0x0000
|
||||
|
||||
#define SERDES_START_WAIT_TIMES 100
|
||||
|
||||
struct emac_reg_write {
|
||||
unsigned int offset;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
static void emac_reg_write_all(void __iomem *base,
|
||||
const struct emac_reg_write *itr, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; ++itr, ++i)
|
||||
writel(itr->val, base + itr->offset);
|
||||
}
|
||||
|
||||
static const struct emac_reg_write physical_coding_sublayer_programming_v1[] = {
|
||||
{EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)},
|
||||
{EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B},
|
||||
{EMAC_SGMII_PHY_CMN_PWR_CTRL,
|
||||
BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN},
|
||||
{EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN},
|
||||
{EMAC_SGMII_PHY_RX_PWR_CTRL,
|
||||
L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN},
|
||||
{EMAC_SGMII_PHY_CMN_PWR_CTRL,
|
||||
BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN |
|
||||
PLL_RXCLK_EN},
|
||||
{EMAC_SGMII_PHY_LANE_CTRL1,
|
||||
L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write sysclk_refclk_setting[] = {
|
||||
{EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS},
|
||||
{EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write pll_setting[] = {
|
||||
{EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)},
|
||||
{EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)},
|
||||
{EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)},
|
||||
{EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)},
|
||||
{EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)},
|
||||
{EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD},
|
||||
{EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)},
|
||||
{EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2},
|
||||
{EMAC_QSERDES_COM_DIV_FRAC_START1,
|
||||
DIV_FRAC_START_MUX | DIV_FRAC_START(85)},
|
||||
{EMAC_QSERDES_COM_DIV_FRAC_START2,
|
||||
DIV_FRAC_START_MUX | DIV_FRAC_START(42)},
|
||||
{EMAC_QSERDES_COM_DIV_FRAC_START3,
|
||||
DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)},
|
||||
{EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)},
|
||||
{EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)},
|
||||
{EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)},
|
||||
{EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN},
|
||||
{EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write cdr_setting[] = {
|
||||
{EMAC_QSERDES_RX_CDR_CONTROL,
|
||||
SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)},
|
||||
{EMAC_QSERDES_RX_CDR_CONTROL2,
|
||||
SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write tx_rx_setting[] = {
|
||||
{EMAC_QSERDES_TX_BIST_MODE_LANENO, 0},
|
||||
{EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)},
|
||||
{EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN},
|
||||
{EMAC_QSERDES_TX_TX_EMP_POST1_LVL,
|
||||
TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)},
|
||||
{EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)},
|
||||
{EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write sgmii_v2_laned[] = {
|
||||
/* CDR Settings */
|
||||
{EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0,
|
||||
UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)},
|
||||
{EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(6)},
|
||||
{EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)},
|
||||
|
||||
/* TX/RX Settings */
|
||||
{EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2},
|
||||
|
||||
{EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE},
|
||||
{EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN},
|
||||
{EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)},
|
||||
{EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX},
|
||||
{EMAC_SGMII_LN_TX_POST, TX_POST_MUX},
|
||||
|
||||
{EMAC_SGMII_LN_CML_CTRL_MODE0,
|
||||
CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)},
|
||||
{EMAC_SGMII_LN_MIXER_CTRL_MODE0,
|
||||
MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)},
|
||||
{EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)},
|
||||
{EMAC_SGMII_LN_SIGDET_ENABLES,
|
||||
SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP},
|
||||
{EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)},
|
||||
|
||||
{EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)},
|
||||
{EMAC_SGMII_LN_RX_MISC_CNTRL0, 0},
|
||||
{EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV,
|
||||
DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)},
|
||||
|
||||
{EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)},
|
||||
{EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)},
|
||||
{EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)},
|
||||
{EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)},
|
||||
{EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)},
|
||||
{EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL},
|
||||
};
|
||||
|
||||
static const struct emac_reg_write physical_coding_sublayer_programming_v2[] = {
|
||||
{EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B},
|
||||
{EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)},
|
||||
{EMAC_SGMII_PHY_TX_PWR_CTRL, 0},
|
||||
{EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE},
|
||||
};
|
||||
|
||||
static int emac_sgmii_link_init(struct emac_adapter *adpt)
|
||||
{
|
||||
struct phy_device *phydev = adpt->phydev;
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
u32 val;
|
||||
|
||||
val = readl(phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2);
|
||||
|
||||
if (phydev->autoneg == AUTONEG_ENABLE) {
|
||||
val &= ~(FORCE_AN_RX_CFG | FORCE_AN_TX_CFG);
|
||||
val |= AN_ENABLE;
|
||||
writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2);
|
||||
} else {
|
||||
u32 speed_cfg;
|
||||
|
||||
switch (phydev->speed) {
|
||||
case SPEED_10:
|
||||
speed_cfg = SPDMODE_10;
|
||||
break;
|
||||
case SPEED_100:
|
||||
speed_cfg = SPDMODE_100;
|
||||
break;
|
||||
case SPEED_1000:
|
||||
speed_cfg = SPDMODE_1000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (phydev->duplex == DUPLEX_FULL)
|
||||
speed_cfg |= DUPLEX_MODE;
|
||||
|
||||
val &= ~AN_ENABLE;
|
||||
writel(speed_cfg, phy->base + EMAC_SGMII_PHY_SPEED_CFG1);
|
||||
writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emac_sgmii_irq_clear(struct emac_adapter *adpt, u32 irq_bits)
|
||||
{
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
u32 status;
|
||||
|
||||
writel_relaxed(irq_bits, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR);
|
||||
writel_relaxed(IRQ_GLOBAL_CLEAR, phy->base + EMAC_SGMII_PHY_IRQ_CMD);
|
||||
/* Ensure interrupt clear command is written to HW */
|
||||
wmb();
|
||||
|
||||
/* After set the IRQ_GLOBAL_CLEAR bit, the status clearing must
|
||||
* be confirmed before clearing the bits in other registers.
|
||||
* It takes a few cycles for hw to clear the interrupt status.
|
||||
*/
|
||||
if (readl_poll_timeout_atomic(phy->base +
|
||||
EMAC_SGMII_PHY_INTERRUPT_STATUS,
|
||||
status, !(status & irq_bits), 1,
|
||||
SGMII_PHY_IRQ_CLR_WAIT_TIME)) {
|
||||
netdev_err(adpt->netdev,
|
||||
"error: failed clear SGMII irq: status:0x%x bits:0x%x\n",
|
||||
status, irq_bits);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Finalize clearing procedure */
|
||||
writel_relaxed(0, phy->base + EMAC_SGMII_PHY_IRQ_CMD);
|
||||
writel_relaxed(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR);
|
||||
|
||||
/* Ensure that clearing procedure finalization is written to HW */
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emac_sgmii_init_v1(struct emac_adapter *adpt)
|
||||
{
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ret = emac_sgmii_link_init(adpt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v1,
|
||||
ARRAY_SIZE(physical_coding_sublayer_programming_v1));
|
||||
emac_reg_write_all(phy->base, sysclk_refclk_setting,
|
||||
ARRAY_SIZE(sysclk_refclk_setting));
|
||||
emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting));
|
||||
emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting));
|
||||
emac_reg_write_all(phy->base, tx_rx_setting,
|
||||
ARRAY_SIZE(tx_rx_setting));
|
||||
|
||||
/* Power up the Ser/Des engine */
|
||||
writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START);
|
||||
|
||||
for (i = 0; i < SERDES_START_WAIT_TIMES; i++) {
|
||||
if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY)
|
||||
break;
|
||||
usleep_range(100, 200);
|
||||
}
|
||||
|
||||
if (i == SERDES_START_WAIT_TIMES) {
|
||||
netdev_err(adpt->netdev, "error: ser/des failed to start\n");
|
||||
return -EIO;
|
||||
}
|
||||
/* Mask out all the SGMII Interrupt */
|
||||
writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK);
|
||||
|
||||
emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emac_sgmii_init_v2(struct emac_adapter *adpt)
|
||||
{
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
void __iomem *phy_regs = phy->base;
|
||||
void __iomem *laned = phy->digital;
|
||||
unsigned int i;
|
||||
u32 lnstatus;
|
||||
int ret;
|
||||
|
||||
ret = emac_sgmii_link_init(adpt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* PCS lane-x init */
|
||||
emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v2,
|
||||
ARRAY_SIZE(physical_coding_sublayer_programming_v2));
|
||||
|
||||
/* SGMII lane-x init */
|
||||
emac_reg_write_all(phy->digital,
|
||||
sgmii_v2_laned, ARRAY_SIZE(sgmii_v2_laned));
|
||||
|
||||
/* Power up PCS and start reset lane state machine */
|
||||
|
||||
writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL);
|
||||
writel(1, laned + SGMII_LN_RSM_START);
|
||||
|
||||
/* Wait for c_ready assertion */
|
||||
for (i = 0; i < SERDES_START_WAIT_TIMES; i++) {
|
||||
lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS);
|
||||
if (lnstatus & BIT(1))
|
||||
break;
|
||||
usleep_range(100, 200);
|
||||
}
|
||||
|
||||
if (i == SERDES_START_WAIT_TIMES) {
|
||||
netdev_err(adpt->netdev, "SGMII failed to start\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Disable digital and SERDES loopback */
|
||||
writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0);
|
||||
writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2);
|
||||
writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1);
|
||||
|
||||
/* Mask out all the SGMII Interrupt */
|
||||
writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK);
|
||||
|
||||
emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emac_sgmii_reset_prepare(struct emac_adapter *adpt)
|
||||
{
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
u32 val;
|
||||
|
||||
/* Reset PHY */
|
||||
val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2);
|
||||
writel(((val & ~PHY_RESET) | PHY_RESET), phy->base +
|
||||
EMAC_EMAC_WRAPPER_CSR2);
|
||||
/* Ensure phy-reset command is written to HW before the release cmd */
|
||||
msleep(50);
|
||||
val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2);
|
||||
writel((val & ~PHY_RESET), phy->base + EMAC_EMAC_WRAPPER_CSR2);
|
||||
/* Ensure phy-reset release command is written to HW before initializing
|
||||
* SGMII
|
||||
*/
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
void emac_sgmii_reset(struct emac_adapter *adpt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000);
|
||||
emac_sgmii_reset_prepare(adpt);
|
||||
|
||||
ret = adpt->phy.initialize(adpt);
|
||||
if (ret)
|
||||
netdev_err(adpt->netdev,
|
||||
"could not reinitialize internal PHY (error=%i)\n",
|
||||
ret);
|
||||
|
||||
clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000);
|
||||
}
|
||||
|
||||
static const struct of_device_id emac_sgmii_dt_match[] = {
|
||||
{
|
||||
.compatible = "qcom,fsm9900-emac-sgmii",
|
||||
.data = emac_sgmii_init_v1,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,qdf2432-emac-sgmii",
|
||||
.data = emac_sgmii_init_v2,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
|
||||
{
|
||||
struct platform_device *sgmii_pdev = NULL;
|
||||
struct emac_phy *phy = &adpt->phy;
|
||||
struct resource *res;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0);
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "missing internal-phy property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sgmii_pdev = of_find_device_by_node(np);
|
||||
if (!sgmii_pdev) {
|
||||
dev_err(&pdev->dev, "invalid internal-phy property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "unrecognized internal phy node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy->initialize = (emac_sgmii_initialize)match->data;
|
||||
|
||||
/* Base address is the first address */
|
||||
res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0);
|
||||
phy->base = devm_ioremap_resource(&sgmii_pdev->dev, res);
|
||||
if (IS_ERR(phy->base))
|
||||
return PTR_ERR(phy->base);
|
||||
|
||||
/* v2 SGMII has a per-lane digital digital, so parse it if it exists */
|
||||
res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
phy->digital = devm_ioremap_resource(&sgmii_pdev->dev, res);
|
||||
if (IS_ERR(phy->base))
|
||||
return PTR_ERR(phy->base);
|
||||
|
||||
}
|
||||
|
||||
return phy->initialize(adpt);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EMAC_SGMII_H_
|
||||
#define _EMAC_SGMII_H_
|
||||
|
||||
struct emac_adapter;
|
||||
struct platform_device;
|
||||
|
||||
int emac_sgmii_init_v1(struct emac_adapter *adpt);
|
||||
int emac_sgmii_init_v2(struct emac_adapter *adpt);
|
||||
int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt);
|
||||
void emac_sgmii_reset(struct emac_adapter *adpt);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,743 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Qualcomm Technologies, Inc. EMAC Gigabit Ethernet Driver */
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "emac.h"
|
||||
#include "emac-mac.h"
|
||||
#include "emac-phy.h"
|
||||
#include "emac-sgmii.h"
|
||||
|
||||
#define EMAC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
|
||||
NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP)
|
||||
|
||||
#define EMAC_RRD_SIZE 4
|
||||
/* The RRD size if timestamping is enabled: */
|
||||
#define EMAC_TS_RRD_SIZE 6
|
||||
#define EMAC_TPD_SIZE 4
|
||||
#define EMAC_RFD_SIZE 2
|
||||
|
||||
#define REG_MAC_RX_STATUS_BIN EMAC_RXMAC_STATC_REG0
|
||||
#define REG_MAC_RX_STATUS_END EMAC_RXMAC_STATC_REG22
|
||||
#define REG_MAC_TX_STATUS_BIN EMAC_TXMAC_STATC_REG0
|
||||
#define REG_MAC_TX_STATUS_END EMAC_TXMAC_STATC_REG24
|
||||
|
||||
#define RXQ0_NUM_RFD_PREF_DEF 8
|
||||
#define TXQ0_NUM_TPD_PREF_DEF 5
|
||||
|
||||
#define EMAC_PREAMBLE_DEF 7
|
||||
|
||||
#define DMAR_DLY_CNT_DEF 15
|
||||
#define DMAW_DLY_CNT_DEF 4
|
||||
|
||||
#define IMR_NORMAL_MASK (\
|
||||
ISR_ERROR |\
|
||||
ISR_GPHY_LINK |\
|
||||
ISR_TX_PKT |\
|
||||
GPHY_WAKEUP_INT)
|
||||
|
||||
#define IMR_EXTENDED_MASK (\
|
||||
SW_MAN_INT |\
|
||||
ISR_OVER |\
|
||||
ISR_ERROR |\
|
||||
ISR_GPHY_LINK |\
|
||||
ISR_TX_PKT |\
|
||||
GPHY_WAKEUP_INT)
|
||||
|
||||
#define ISR_TX_PKT (\
|
||||
TX_PKT_INT |\
|
||||
TX_PKT_INT1 |\
|
||||
TX_PKT_INT2 |\
|
||||
TX_PKT_INT3)
|
||||
|
||||
#define ISR_GPHY_LINK (\
|
||||
GPHY_LINK_UP_INT |\
|
||||
GPHY_LINK_DOWN_INT)
|
||||
|
||||
#define ISR_OVER (\
|
||||
RFD0_UR_INT |\
|
||||
RFD1_UR_INT |\
|
||||
RFD2_UR_INT |\
|
||||
RFD3_UR_INT |\
|
||||
RFD4_UR_INT |\
|
||||
RXF_OF_INT |\
|
||||
TXF_UR_INT)
|
||||
|
||||
#define ISR_ERROR (\
|
||||
DMAR_TO_INT |\
|
||||
DMAW_TO_INT |\
|
||||
TXQ_TO_INT)
|
||||
|
||||
/* in sync with enum emac_clk_id */
|
||||
static const char * const emac_clk_name[] = {
|
||||
"axi_clk", "cfg_ahb_clk", "high_speed_clk", "mdio_clk", "tx_clk",
|
||||
"rx_clk", "sys_clk"
|
||||
};
|
||||
|
||||
void emac_reg_update32(void __iomem *addr, u32 mask, u32 val)
|
||||
{
|
||||
u32 data = readl(addr);
|
||||
|
||||
writel(((data & ~mask) | val), addr);
|
||||
}
|
||||
|
||||
/* reinitialize */
|
||||
int emac_reinit_locked(struct emac_adapter *adpt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adpt->reset_lock);
|
||||
|
||||
emac_mac_down(adpt);
|
||||
emac_sgmii_reset(adpt);
|
||||
ret = emac_mac_up(adpt);
|
||||
|
||||
mutex_unlock(&adpt->reset_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* NAPI */
|
||||
static int emac_napi_rtx(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct emac_rx_queue *rx_q =
|
||||
container_of(napi, struct emac_rx_queue, napi);
|
||||
struct emac_adapter *adpt = netdev_priv(rx_q->netdev);
|
||||
struct emac_irq *irq = rx_q->irq;
|
||||
int work_done = 0;
|
||||
|
||||
emac_mac_rx_process(adpt, rx_q, &work_done, budget);
|
||||
|
||||
if (work_done < budget) {
|
||||
napi_complete(napi);
|
||||
|
||||
irq->mask |= rx_q->intr;
|
||||
writel(irq->mask, adpt->base + EMAC_INT_MASK);
|
||||
}
|
||||
|
||||
return work_done;
|
||||
}
|
||||
|
||||
/* Transmit the packet */
|
||||
static int emac_start_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
return emac_mac_tx_buf_send(adpt, &adpt->tx_q, skb);
|
||||
}
|
||||
|
||||
irqreturn_t emac_isr(int _irq, void *data)
|
||||
{
|
||||
struct emac_irq *irq = data;
|
||||
struct emac_adapter *adpt =
|
||||
container_of(irq, struct emac_adapter, irq);
|
||||
struct emac_rx_queue *rx_q = &adpt->rx_q;
|
||||
u32 isr, status;
|
||||
|
||||
/* disable the interrupt */
|
||||
writel(0, adpt->base + EMAC_INT_MASK);
|
||||
|
||||
isr = readl_relaxed(adpt->base + EMAC_INT_STATUS);
|
||||
|
||||
status = isr & irq->mask;
|
||||
if (status == 0)
|
||||
goto exit;
|
||||
|
||||
if (status & ISR_ERROR) {
|
||||
netif_warn(adpt, intr, adpt->netdev,
|
||||
"warning: error irq status 0x%lx\n",
|
||||
status & ISR_ERROR);
|
||||
/* reset MAC */
|
||||
schedule_work(&adpt->work_thread);
|
||||
}
|
||||
|
||||
/* Schedule the napi for receive queue with interrupt
|
||||
* status bit set
|
||||
*/
|
||||
if (status & rx_q->intr) {
|
||||
if (napi_schedule_prep(&rx_q->napi)) {
|
||||
irq->mask &= ~rx_q->intr;
|
||||
__napi_schedule(&rx_q->napi);
|
||||
}
|
||||
}
|
||||
|
||||
if (status & TX_PKT_INT)
|
||||
emac_mac_tx_process(adpt, &adpt->tx_q);
|
||||
|
||||
if (status & ISR_OVER)
|
||||
net_warn_ratelimited("warning: TX/RX overflow\n");
|
||||
|
||||
/* link event */
|
||||
if (status & ISR_GPHY_LINK)
|
||||
phy_mac_interrupt(adpt->phydev, !!(status & GPHY_LINK_UP_INT));
|
||||
|
||||
exit:
|
||||
/* enable the interrupt */
|
||||
writel(irq->mask, adpt->base + EMAC_INT_MASK);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Configure VLAN tag strip/insert feature */
|
||||
static int emac_set_features(struct net_device *netdev,
|
||||
netdev_features_t features)
|
||||
{
|
||||
netdev_features_t changed = features ^ netdev->features;
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
/* We only need to reprogram the hardware if the VLAN tag features
|
||||
* have changed, and if it's already running.
|
||||
*/
|
||||
if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX)))
|
||||
return 0;
|
||||
|
||||
if (!netif_running(netdev))
|
||||
return 0;
|
||||
|
||||
/* emac_mac_mode_config() uses netdev->features to configure the EMAC,
|
||||
* so make sure it's set first.
|
||||
*/
|
||||
netdev->features = features;
|
||||
|
||||
return emac_reinit_locked(adpt);
|
||||
}
|
||||
|
||||
/* Configure Multicast and Promiscuous modes */
|
||||
static void emac_rx_mode_set(struct net_device *netdev)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
emac_mac_mode_config(adpt);
|
||||
|
||||
/* update multicast address filtering */
|
||||
emac_mac_multicast_addr_clear(adpt);
|
||||
netdev_for_each_mc_addr(ha, netdev)
|
||||
emac_mac_multicast_addr_set(adpt, ha->addr);
|
||||
}
|
||||
|
||||
/* Change the Maximum Transfer Unit (MTU) */
|
||||
static int emac_change_mtu(struct net_device *netdev, int new_mtu)
|
||||
{
|
||||
unsigned int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
if ((max_frame < EMAC_MIN_ETH_FRAME_SIZE) ||
|
||||
(max_frame > EMAC_MAX_ETH_FRAME_SIZE)) {
|
||||
netdev_err(adpt->netdev, "error: invalid MTU setting\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
netif_info(adpt, hw, adpt->netdev,
|
||||
"changing MTU from %d to %d\n", netdev->mtu,
|
||||
new_mtu);
|
||||
netdev->mtu = new_mtu;
|
||||
|
||||
if (netif_running(netdev))
|
||||
return emac_reinit_locked(adpt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when the network interface is made active */
|
||||
static int emac_open(struct net_device *netdev)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
int ret;
|
||||
|
||||
/* allocate rx/tx dma buffer & descriptors */
|
||||
ret = emac_mac_rx_tx_rings_alloc_all(adpt);
|
||||
if (ret) {
|
||||
netdev_err(adpt->netdev, "error allocating rx/tx rings\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = emac_mac_up(adpt);
|
||||
if (ret) {
|
||||
emac_mac_rx_tx_rings_free_all(adpt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
emac_mac_start(adpt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when the network interface is disabled */
|
||||
static int emac_close(struct net_device *netdev)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
mutex_lock(&adpt->reset_lock);
|
||||
|
||||
emac_mac_down(adpt);
|
||||
emac_mac_rx_tx_rings_free_all(adpt);
|
||||
|
||||
mutex_unlock(&adpt->reset_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Respond to a TX hang */
|
||||
static void emac_tx_timeout(struct net_device *netdev)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
schedule_work(&adpt->work_thread);
|
||||
}
|
||||
|
||||
/* IOCTL support for the interface */
|
||||
static int emac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
if (!netif_running(netdev))
|
||||
return -EINVAL;
|
||||
|
||||
if (!netdev->phydev)
|
||||
return -ENODEV;
|
||||
|
||||
return phy_mii_ioctl(netdev->phydev, ifr, cmd);
|
||||
}
|
||||
|
||||
/* Provide network statistics info for the interface */
|
||||
static struct rtnl_link_stats64 *emac_get_stats64(struct net_device *netdev,
|
||||
struct rtnl_link_stats64 *net_stats)
|
||||
{
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
unsigned int addr = REG_MAC_RX_STATUS_BIN;
|
||||
struct emac_stats *stats = &adpt->stats;
|
||||
u64 *stats_itr = &adpt->stats.rx_ok;
|
||||
u32 val;
|
||||
|
||||
spin_lock(&stats->lock);
|
||||
|
||||
while (addr <= REG_MAC_RX_STATUS_END) {
|
||||
val = readl_relaxed(adpt->base + addr);
|
||||
*stats_itr += val;
|
||||
stats_itr++;
|
||||
addr += sizeof(u32);
|
||||
}
|
||||
|
||||
/* additional rx status */
|
||||
val = readl_relaxed(adpt->base + EMAC_RXMAC_STATC_REG23);
|
||||
adpt->stats.rx_crc_align += val;
|
||||
val = readl_relaxed(adpt->base + EMAC_RXMAC_STATC_REG24);
|
||||
adpt->stats.rx_jabbers += val;
|
||||
|
||||
/* update tx status */
|
||||
addr = REG_MAC_TX_STATUS_BIN;
|
||||
stats_itr = &adpt->stats.tx_ok;
|
||||
|
||||
while (addr <= REG_MAC_TX_STATUS_END) {
|
||||
val = readl_relaxed(adpt->base + addr);
|
||||
*stats_itr += val;
|
||||
++stats_itr;
|
||||
addr += sizeof(u32);
|
||||
}
|
||||
|
||||
/* additional tx status */
|
||||
val = readl_relaxed(adpt->base + EMAC_TXMAC_STATC_REG25);
|
||||
adpt->stats.tx_col += val;
|
||||
|
||||
/* return parsed statistics */
|
||||
net_stats->rx_packets = stats->rx_ok;
|
||||
net_stats->tx_packets = stats->tx_ok;
|
||||
net_stats->rx_bytes = stats->rx_byte_cnt;
|
||||
net_stats->tx_bytes = stats->tx_byte_cnt;
|
||||
net_stats->multicast = stats->rx_mcast;
|
||||
net_stats->collisions = stats->tx_1_col + stats->tx_2_col * 2 +
|
||||
stats->tx_late_col + stats->tx_abort_col;
|
||||
|
||||
net_stats->rx_errors = stats->rx_frag + stats->rx_fcs_err +
|
||||
stats->rx_len_err + stats->rx_sz_ov +
|
||||
stats->rx_align_err;
|
||||
net_stats->rx_fifo_errors = stats->rx_rxf_ov;
|
||||
net_stats->rx_length_errors = stats->rx_len_err;
|
||||
net_stats->rx_crc_errors = stats->rx_fcs_err;
|
||||
net_stats->rx_frame_errors = stats->rx_align_err;
|
||||
net_stats->rx_over_errors = stats->rx_rxf_ov;
|
||||
net_stats->rx_missed_errors = stats->rx_rxf_ov;
|
||||
|
||||
net_stats->tx_errors = stats->tx_late_col + stats->tx_abort_col +
|
||||
stats->tx_underrun + stats->tx_trunc;
|
||||
net_stats->tx_fifo_errors = stats->tx_underrun;
|
||||
net_stats->tx_aborted_errors = stats->tx_abort_col;
|
||||
net_stats->tx_window_errors = stats->tx_late_col;
|
||||
|
||||
spin_unlock(&stats->lock);
|
||||
|
||||
return net_stats;
|
||||
}
|
||||
|
||||
static const struct net_device_ops emac_netdev_ops = {
|
||||
.ndo_open = emac_open,
|
||||
.ndo_stop = emac_close,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_start_xmit = emac_start_xmit,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_change_mtu = emac_change_mtu,
|
||||
.ndo_do_ioctl = emac_ioctl,
|
||||
.ndo_tx_timeout = emac_tx_timeout,
|
||||
.ndo_get_stats64 = emac_get_stats64,
|
||||
.ndo_set_features = emac_set_features,
|
||||
.ndo_set_rx_mode = emac_rx_mode_set,
|
||||
};
|
||||
|
||||
/* Watchdog task routine, called to reinitialize the EMAC */
|
||||
static void emac_work_thread(struct work_struct *work)
|
||||
{
|
||||
struct emac_adapter *adpt =
|
||||
container_of(work, struct emac_adapter, work_thread);
|
||||
|
||||
emac_reinit_locked(adpt);
|
||||
}
|
||||
|
||||
/* Initialize various data structures */
|
||||
static void emac_init_adapter(struct emac_adapter *adpt)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* descriptors */
|
||||
adpt->tx_desc_cnt = EMAC_DEF_TX_DESCS;
|
||||
adpt->rx_desc_cnt = EMAC_DEF_RX_DESCS;
|
||||
|
||||
/* dma */
|
||||
adpt->dma_order = emac_dma_ord_out;
|
||||
adpt->dmar_block = emac_dma_req_4096;
|
||||
adpt->dmaw_block = emac_dma_req_128;
|
||||
adpt->dmar_dly_cnt = DMAR_DLY_CNT_DEF;
|
||||
adpt->dmaw_dly_cnt = DMAW_DLY_CNT_DEF;
|
||||
adpt->tpd_burst = TXQ0_NUM_TPD_PREF_DEF;
|
||||
adpt->rfd_burst = RXQ0_NUM_RFD_PREF_DEF;
|
||||
|
||||
/* irq moderator */
|
||||
reg = ((EMAC_DEF_RX_IRQ_MOD >> 1) << IRQ_MODERATOR2_INIT_SHFT) |
|
||||
((EMAC_DEF_TX_IRQ_MOD >> 1) << IRQ_MODERATOR_INIT_SHFT);
|
||||
adpt->irq_mod = reg;
|
||||
|
||||
/* others */
|
||||
adpt->preamble = EMAC_PREAMBLE_DEF;
|
||||
}
|
||||
|
||||
/* Get the clock */
|
||||
static int emac_clks_get(struct platform_device *pdev,
|
||||
struct emac_adapter *adpt)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < EMAC_CLK_CNT; i++) {
|
||||
struct clk *clk = devm_clk_get(&pdev->dev, emac_clk_name[i]);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"could not claim clock %s (error=%li)\n",
|
||||
emac_clk_name[i], PTR_ERR(clk));
|
||||
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
adpt->clk[i] = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize clocks */
|
||||
static int emac_clks_phase1_init(struct platform_device *pdev,
|
||||
struct emac_adapter *adpt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = emac_clks_get(pdev, adpt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adpt->clk[EMAC_CLK_AXI]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adpt->clk[EMAC_CLK_CFG_AHB]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_prepare_enable(adpt->clk[EMAC_CLK_HIGH_SPEED]);
|
||||
}
|
||||
|
||||
/* Enable clocks; needs emac_clks_phase1_init to be called before */
|
||||
static int emac_clks_phase2_init(struct platform_device *pdev,
|
||||
struct emac_adapter *adpt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(adpt->clk[EMAC_CLK_TX], 125000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adpt->clk[EMAC_CLK_TX]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_set_rate(adpt->clk[EMAC_CLK_MDIO], 25000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adpt->clk[EMAC_CLK_MDIO]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adpt->clk[EMAC_CLK_RX]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_prepare_enable(adpt->clk[EMAC_CLK_SYS]);
|
||||
}
|
||||
|
||||
static void emac_clks_teardown(struct emac_adapter *adpt)
|
||||
{
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < EMAC_CLK_CNT; i++)
|
||||
clk_disable_unprepare(adpt->clk[i]);
|
||||
}
|
||||
|
||||
/* Get the resources */
|
||||
static int emac_probe_resources(struct platform_device *pdev,
|
||||
struct emac_adapter *adpt)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct net_device *netdev = adpt->netdev;
|
||||
struct resource *res;
|
||||
const void *maddr;
|
||||
int ret = 0;
|
||||
|
||||
/* get mac address */
|
||||
maddr = of_get_mac_address(node);
|
||||
if (!maddr)
|
||||
eth_hw_addr_random(netdev);
|
||||
else
|
||||
ether_addr_copy(netdev->dev_addr, maddr);
|
||||
|
||||
/* Core 0 interrupt */
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"error: missing core0 irq resource (error=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
adpt->irq.irq = ret;
|
||||
|
||||
/* base register address */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adpt->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adpt->base))
|
||||
return PTR_ERR(adpt->base);
|
||||
|
||||
/* CSR register address */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
adpt->csr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adpt->csr))
|
||||
return PTR_ERR(adpt->csr);
|
||||
|
||||
netdev->base_addr = (unsigned long)adpt->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id emac_dt_match[] = {
|
||||
{
|
||||
.compatible = "qcom,fsm9900-emac",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int emac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev;
|
||||
struct emac_adapter *adpt;
|
||||
struct emac_phy *phy;
|
||||
u16 devid, revid;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* The EMAC itself is capable of 64-bit DMA, so try that first. */
|
||||
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (ret) {
|
||||
/* Some platforms may restrict the EMAC's address bus to less
|
||||
* then the size of DDR. In this case, we need to try a
|
||||
* smaller mask. We could try every possible smaller mask,
|
||||
* but that's overkill. Instead, just fall to 32-bit, which
|
||||
* should always work.
|
||||
*/
|
||||
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not set DMA mask\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
netdev = alloc_etherdev(sizeof(struct emac_adapter));
|
||||
if (!netdev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, netdev);
|
||||
SET_NETDEV_DEV(netdev, &pdev->dev);
|
||||
|
||||
adpt = netdev_priv(netdev);
|
||||
adpt->netdev = netdev;
|
||||
adpt->msg_enable = EMAC_MSG_DEFAULT;
|
||||
|
||||
phy = &adpt->phy;
|
||||
|
||||
mutex_init(&adpt->reset_lock);
|
||||
spin_lock_init(&adpt->stats.lock);
|
||||
|
||||
adpt->irq.mask = RX_PKT_INT0 | IMR_NORMAL_MASK;
|
||||
|
||||
ret = emac_probe_resources(pdev, adpt);
|
||||
if (ret)
|
||||
goto err_undo_netdev;
|
||||
|
||||
/* initialize clocks */
|
||||
ret = emac_clks_phase1_init(pdev, adpt);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not initialize clocks\n");
|
||||
goto err_undo_netdev;
|
||||
}
|
||||
|
||||
netdev->watchdog_timeo = EMAC_WATCHDOG_TIME;
|
||||
netdev->irq = adpt->irq.irq;
|
||||
|
||||
adpt->rrd_size = EMAC_RRD_SIZE;
|
||||
adpt->tpd_size = EMAC_TPD_SIZE;
|
||||
adpt->rfd_size = EMAC_RFD_SIZE;
|
||||
|
||||
netdev->netdev_ops = &emac_netdev_ops;
|
||||
|
||||
emac_init_adapter(adpt);
|
||||
|
||||
/* init external phy */
|
||||
ret = emac_phy_config(pdev, adpt);
|
||||
if (ret)
|
||||
goto err_undo_clocks;
|
||||
|
||||
/* init internal sgmii phy */
|
||||
ret = emac_sgmii_config(pdev, adpt);
|
||||
if (ret)
|
||||
goto err_undo_mdiobus;
|
||||
|
||||
/* enable clocks */
|
||||
ret = emac_clks_phase2_init(pdev, adpt);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not initialize clocks\n");
|
||||
goto err_undo_mdiobus;
|
||||
}
|
||||
|
||||
emac_mac_reset(adpt);
|
||||
|
||||
/* set hw features */
|
||||
netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
|
||||
NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX |
|
||||
NETIF_F_HW_VLAN_CTAG_TX;
|
||||
netdev->hw_features = netdev->features;
|
||||
|
||||
netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM |
|
||||
NETIF_F_TSO | NETIF_F_TSO6;
|
||||
|
||||
INIT_WORK(&adpt->work_thread, emac_work_thread);
|
||||
|
||||
/* Initialize queues */
|
||||
emac_mac_rx_tx_ring_init_all(pdev, adpt);
|
||||
|
||||
netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx,
|
||||
NAPI_POLL_WEIGHT);
|
||||
|
||||
ret = register_netdev(netdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register net device\n");
|
||||
goto err_undo_napi;
|
||||
}
|
||||
|
||||
reg = readl_relaxed(adpt->base + EMAC_DMA_MAS_CTRL);
|
||||
devid = (reg & DEV_ID_NUM_BMSK) >> DEV_ID_NUM_SHFT;
|
||||
revid = (reg & DEV_REV_NUM_BMSK) >> DEV_REV_NUM_SHFT;
|
||||
reg = readl_relaxed(adpt->base + EMAC_CORE_HW_VERSION);
|
||||
|
||||
netif_info(adpt, probe, netdev,
|
||||
"hardware id %d.%d, hardware version %d.%d.%d\n",
|
||||
devid, revid,
|
||||
(reg & MAJOR_BMSK) >> MAJOR_SHFT,
|
||||
(reg & MINOR_BMSK) >> MINOR_SHFT,
|
||||
(reg & STEP_BMSK) >> STEP_SHFT);
|
||||
|
||||
return 0;
|
||||
|
||||
err_undo_napi:
|
||||
netif_napi_del(&adpt->rx_q.napi);
|
||||
err_undo_mdiobus:
|
||||
mdiobus_unregister(adpt->mii_bus);
|
||||
err_undo_clocks:
|
||||
emac_clks_teardown(adpt);
|
||||
err_undo_netdev:
|
||||
free_netdev(netdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev = dev_get_drvdata(&pdev->dev);
|
||||
struct emac_adapter *adpt = netdev_priv(netdev);
|
||||
|
||||
unregister_netdev(netdev);
|
||||
netif_napi_del(&adpt->rx_q.napi);
|
||||
|
||||
emac_clks_teardown(adpt);
|
||||
|
||||
mdiobus_unregister(adpt->mii_bus);
|
||||
free_netdev(netdev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver emac_platform_driver = {
|
||||
.probe = emac_probe,
|
||||
.remove = emac_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "qcom-emac",
|
||||
.of_match_table = emac_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(emac_platform_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:qcom-emac");
|
|
@ -0,0 +1,335 @@
|
|||
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EMAC_H_
|
||||
#define _EMAC_H_
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "emac-mac.h"
|
||||
#include "emac-phy.h"
|
||||
|
||||
/* EMAC base register offsets */
|
||||
#define EMAC_DMA_MAS_CTRL 0x001400
|
||||
#define EMAC_IRQ_MOD_TIM_INIT 0x001408
|
||||
#define EMAC_BLK_IDLE_STS 0x00140c
|
||||
#define EMAC_PHY_LINK_DELAY 0x00141c
|
||||
#define EMAC_SYS_ALIV_CTRL 0x001434
|
||||
#define EMAC_MAC_IPGIFG_CTRL 0x001484
|
||||
#define EMAC_MAC_STA_ADDR0 0x001488
|
||||
#define EMAC_MAC_STA_ADDR1 0x00148c
|
||||
#define EMAC_HASH_TAB_REG0 0x001490
|
||||
#define EMAC_HASH_TAB_REG1 0x001494
|
||||
#define EMAC_MAC_HALF_DPLX_CTRL 0x001498
|
||||
#define EMAC_MAX_FRAM_LEN_CTRL 0x00149c
|
||||
#define EMAC_INT_STATUS 0x001600
|
||||
#define EMAC_INT_MASK 0x001604
|
||||
#define EMAC_RXMAC_STATC_REG0 0x001700
|
||||
#define EMAC_RXMAC_STATC_REG22 0x001758
|
||||
#define EMAC_TXMAC_STATC_REG0 0x001760
|
||||
#define EMAC_TXMAC_STATC_REG24 0x0017c0
|
||||
#define EMAC_CORE_HW_VERSION 0x001974
|
||||
#define EMAC_IDT_TABLE0 0x001b00
|
||||
#define EMAC_RXMAC_STATC_REG23 0x001bc8
|
||||
#define EMAC_RXMAC_STATC_REG24 0x001bcc
|
||||
#define EMAC_TXMAC_STATC_REG25 0x001bd0
|
||||
#define EMAC_INT1_MASK 0x001bf0
|
||||
#define EMAC_INT1_STATUS 0x001bf4
|
||||
#define EMAC_INT2_MASK 0x001bf8
|
||||
#define EMAC_INT2_STATUS 0x001bfc
|
||||
#define EMAC_INT3_MASK 0x001c00
|
||||
#define EMAC_INT3_STATUS 0x001c04
|
||||
|
||||
/* EMAC_DMA_MAS_CTRL */
|
||||
#define DEV_ID_NUM_BMSK 0x7f000000
|
||||
#define DEV_ID_NUM_SHFT 24
|
||||
#define DEV_REV_NUM_BMSK 0xff0000
|
||||
#define DEV_REV_NUM_SHFT 16
|
||||
#define INT_RD_CLR_EN 0x4000
|
||||
#define IRQ_MODERATOR2_EN 0x800
|
||||
#define IRQ_MODERATOR_EN 0x400
|
||||
#define LPW_CLK_SEL 0x80
|
||||
#define LPW_STATE 0x20
|
||||
#define LPW_MODE 0x10
|
||||
#define SOFT_RST 0x1
|
||||
|
||||
/* EMAC_IRQ_MOD_TIM_INIT */
|
||||
#define IRQ_MODERATOR2_INIT_BMSK 0xffff0000
|
||||
#define IRQ_MODERATOR2_INIT_SHFT 16
|
||||
#define IRQ_MODERATOR_INIT_BMSK 0xffff
|
||||
#define IRQ_MODERATOR_INIT_SHFT 0
|
||||
|
||||
/* EMAC_INT_STATUS */
|
||||
#define DIS_INT BIT(31)
|
||||
#define PTP_INT BIT(30)
|
||||
#define RFD4_UR_INT BIT(29)
|
||||
#define TX_PKT_INT3 BIT(26)
|
||||
#define TX_PKT_INT2 BIT(25)
|
||||
#define TX_PKT_INT1 BIT(24)
|
||||
#define RX_PKT_INT3 BIT(19)
|
||||
#define RX_PKT_INT2 BIT(18)
|
||||
#define RX_PKT_INT1 BIT(17)
|
||||
#define RX_PKT_INT0 BIT(16)
|
||||
#define TX_PKT_INT BIT(15)
|
||||
#define TXQ_TO_INT BIT(14)
|
||||
#define GPHY_WAKEUP_INT BIT(13)
|
||||
#define GPHY_LINK_DOWN_INT BIT(12)
|
||||
#define GPHY_LINK_UP_INT BIT(11)
|
||||
#define DMAW_TO_INT BIT(10)
|
||||
#define DMAR_TO_INT BIT(9)
|
||||
#define TXF_UR_INT BIT(8)
|
||||
#define RFD3_UR_INT BIT(7)
|
||||
#define RFD2_UR_INT BIT(6)
|
||||
#define RFD1_UR_INT BIT(5)
|
||||
#define RFD0_UR_INT BIT(4)
|
||||
#define RXF_OF_INT BIT(3)
|
||||
#define SW_MAN_INT BIT(2)
|
||||
|
||||
/* EMAC_MAILBOX_6 */
|
||||
#define RFD2_PROC_IDX_BMSK 0xfff0000
|
||||
#define RFD2_PROC_IDX_SHFT 16
|
||||
#define RFD2_PROD_IDX_BMSK 0xfff
|
||||
#define RFD2_PROD_IDX_SHFT 0
|
||||
|
||||
/* EMAC_CORE_HW_VERSION */
|
||||
#define MAJOR_BMSK 0xf0000000
|
||||
#define MAJOR_SHFT 28
|
||||
#define MINOR_BMSK 0xfff0000
|
||||
#define MINOR_SHFT 16
|
||||
#define STEP_BMSK 0xffff
|
||||
#define STEP_SHFT 0
|
||||
|
||||
/* EMAC_EMAC_WRAPPER_CSR1 */
|
||||
#define TX_INDX_FIFO_SYNC_RST BIT(23)
|
||||
#define TX_TS_FIFO_SYNC_RST BIT(22)
|
||||
#define RX_TS_FIFO2_SYNC_RST BIT(21)
|
||||
#define RX_TS_FIFO1_SYNC_RST BIT(20)
|
||||
#define TX_TS_ENABLE BIT(16)
|
||||
#define DIS_1588_CLKS BIT(11)
|
||||
#define FREQ_MODE BIT(9)
|
||||
#define ENABLE_RRD_TIMESTAMP BIT(3)
|
||||
|
||||
/* EMAC_EMAC_WRAPPER_CSR2 */
|
||||
#define HDRIVE_BMSK 0x3000
|
||||
#define HDRIVE_SHFT 12
|
||||
#define SLB_EN BIT(9)
|
||||
#define PLB_EN BIT(8)
|
||||
#define WOL_EN BIT(3)
|
||||
#define PHY_RESET BIT(0)
|
||||
|
||||
#define EMAC_DEV_ID 0x0040
|
||||
|
||||
/* SGMII v2 per lane registers */
|
||||
#define SGMII_LN_RSM_START 0x029C
|
||||
|
||||
/* SGMII v2 PHY common registers */
|
||||
#define SGMII_PHY_CMN_CTRL 0x0408
|
||||
#define SGMII_PHY_CMN_RESET_CTRL 0x0410
|
||||
|
||||
/* SGMII v2 PHY registers per lane */
|
||||
#define SGMII_PHY_LN_OFFSET 0x0400
|
||||
#define SGMII_PHY_LN_LANE_STATUS 0x00DC
|
||||
#define SGMII_PHY_LN_BIST_GEN0 0x008C
|
||||
#define SGMII_PHY_LN_BIST_GEN1 0x0090
|
||||
#define SGMII_PHY_LN_BIST_GEN2 0x0094
|
||||
#define SGMII_PHY_LN_BIST_GEN3 0x0098
|
||||
#define SGMII_PHY_LN_CDR_CTRL1 0x005C
|
||||
|
||||
enum emac_clk_id {
|
||||
EMAC_CLK_AXI,
|
||||
EMAC_CLK_CFG_AHB,
|
||||
EMAC_CLK_HIGH_SPEED,
|
||||
EMAC_CLK_MDIO,
|
||||
EMAC_CLK_TX,
|
||||
EMAC_CLK_RX,
|
||||
EMAC_CLK_SYS,
|
||||
EMAC_CLK_CNT
|
||||
};
|
||||
|
||||
#define EMAC_LINK_SPEED_UNKNOWN 0x0
|
||||
#define EMAC_LINK_SPEED_10_HALF BIT(0)
|
||||
#define EMAC_LINK_SPEED_10_FULL BIT(1)
|
||||
#define EMAC_LINK_SPEED_100_HALF BIT(2)
|
||||
#define EMAC_LINK_SPEED_100_FULL BIT(3)
|
||||
#define EMAC_LINK_SPEED_1GB_FULL BIT(5)
|
||||
|
||||
#define EMAC_MAX_SETUP_LNK_CYCLE 100
|
||||
|
||||
/* Wake On Lan */
|
||||
#define EMAC_WOL_PHY 0x00000001 /* PHY Status Change */
|
||||
#define EMAC_WOL_MAGIC 0x00000002 /* Magic Packet */
|
||||
|
||||
struct emac_stats {
|
||||
/* rx */
|
||||
u64 rx_ok; /* good packets */
|
||||
u64 rx_bcast; /* good broadcast packets */
|
||||
u64 rx_mcast; /* good multicast packets */
|
||||
u64 rx_pause; /* pause packet */
|
||||
u64 rx_ctrl; /* control packets other than pause frame. */
|
||||
u64 rx_fcs_err; /* packets with bad FCS. */
|
||||
u64 rx_len_err; /* packets with length mismatch */
|
||||
u64 rx_byte_cnt; /* good bytes count (without FCS) */
|
||||
u64 rx_runt; /* runt packets */
|
||||
u64 rx_frag; /* fragment count */
|
||||
u64 rx_sz_64; /* packets that are 64 bytes */
|
||||
u64 rx_sz_65_127; /* packets that are 65-127 bytes */
|
||||
u64 rx_sz_128_255; /* packets that are 128-255 bytes */
|
||||
u64 rx_sz_256_511; /* packets that are 256-511 bytes */
|
||||
u64 rx_sz_512_1023; /* packets that are 512-1023 bytes */
|
||||
u64 rx_sz_1024_1518; /* packets that are 1024-1518 bytes */
|
||||
u64 rx_sz_1519_max; /* packets that are 1519-MTU bytes*/
|
||||
u64 rx_sz_ov; /* packets that are >MTU bytes (truncated) */
|
||||
u64 rx_rxf_ov; /* packets dropped due to RX FIFO overflow */
|
||||
u64 rx_align_err; /* alignment errors */
|
||||
u64 rx_bcast_byte_cnt; /* broadcast packets byte count (without FCS) */
|
||||
u64 rx_mcast_byte_cnt; /* multicast packets byte count (without FCS) */
|
||||
u64 rx_err_addr; /* packets dropped due to address filtering */
|
||||
u64 rx_crc_align; /* CRC align errors */
|
||||
u64 rx_jabbers; /* jabbers */
|
||||
|
||||
/* tx */
|
||||
u64 tx_ok; /* good packets */
|
||||
u64 tx_bcast; /* good broadcast packets */
|
||||
u64 tx_mcast; /* good multicast packets */
|
||||
u64 tx_pause; /* pause packets */
|
||||
u64 tx_exc_defer; /* packets with excessive deferral */
|
||||
u64 tx_ctrl; /* control packets other than pause frame */
|
||||
u64 tx_defer; /* packets that are deferred. */
|
||||
u64 tx_byte_cnt; /* good bytes count (without FCS) */
|
||||
u64 tx_sz_64; /* packets that are 64 bytes */
|
||||
u64 tx_sz_65_127; /* packets that are 65-127 bytes */
|
||||
u64 tx_sz_128_255; /* packets that are 128-255 bytes */
|
||||
u64 tx_sz_256_511; /* packets that are 256-511 bytes */
|
||||
u64 tx_sz_512_1023; /* packets that are 512-1023 bytes */
|
||||
u64 tx_sz_1024_1518; /* packets that are 1024-1518 bytes */
|
||||
u64 tx_sz_1519_max; /* packets that are 1519-MTU bytes */
|
||||
u64 tx_1_col; /* packets single prior collision */
|
||||
u64 tx_2_col; /* packets with multiple prior collisions */
|
||||
u64 tx_late_col; /* packets with late collisions */
|
||||
u64 tx_abort_col; /* packets aborted due to excess collisions */
|
||||
u64 tx_underrun; /* packets aborted due to FIFO underrun */
|
||||
u64 tx_rd_eop; /* count of reads beyond EOP */
|
||||
u64 tx_len_err; /* packets with length mismatch */
|
||||
u64 tx_trunc; /* packets truncated due to size >MTU */
|
||||
u64 tx_bcast_byte; /* broadcast packets byte count (without FCS) */
|
||||
u64 tx_mcast_byte; /* multicast packets byte count (without FCS) */
|
||||
u64 tx_col; /* collisions */
|
||||
|
||||
spinlock_t lock; /* prevent multiple simultaneous readers */
|
||||
};
|
||||
|
||||
/* RSS hstype Definitions */
|
||||
#define EMAC_RSS_HSTYP_IPV4_EN 0x00000001
|
||||
#define EMAC_RSS_HSTYP_TCP4_EN 0x00000002
|
||||
#define EMAC_RSS_HSTYP_IPV6_EN 0x00000004
|
||||
#define EMAC_RSS_HSTYP_TCP6_EN 0x00000008
|
||||
#define EMAC_RSS_HSTYP_ALL_EN (\
|
||||
EMAC_RSS_HSTYP_IPV4_EN |\
|
||||
EMAC_RSS_HSTYP_TCP4_EN |\
|
||||
EMAC_RSS_HSTYP_IPV6_EN |\
|
||||
EMAC_RSS_HSTYP_TCP6_EN)
|
||||
|
||||
#define EMAC_VLAN_TO_TAG(_vlan, _tag) \
|
||||
(_tag = ((((_vlan) >> 8) & 0xFF) | (((_vlan) & 0xFF) << 8)))
|
||||
|
||||
#define EMAC_TAG_TO_VLAN(_tag, _vlan) \
|
||||
(_vlan = ((((_tag) >> 8) & 0xFF) | (((_tag) & 0xFF) << 8)))
|
||||
|
||||
#define EMAC_DEF_RX_BUF_SIZE 1536
|
||||
#define EMAC_MAX_JUMBO_PKT_SIZE (9 * 1024)
|
||||
#define EMAC_MAX_TX_OFFLOAD_THRESH (9 * 1024)
|
||||
|
||||
#define EMAC_MAX_ETH_FRAME_SIZE EMAC_MAX_JUMBO_PKT_SIZE
|
||||
#define EMAC_MIN_ETH_FRAME_SIZE 68
|
||||
|
||||
#define EMAC_DEF_TX_QUEUES 1
|
||||
#define EMAC_DEF_RX_QUEUES 1
|
||||
|
||||
#define EMAC_MIN_TX_DESCS 128
|
||||
#define EMAC_MIN_RX_DESCS 128
|
||||
|
||||
#define EMAC_MAX_TX_DESCS 16383
|
||||
#define EMAC_MAX_RX_DESCS 2047
|
||||
|
||||
#define EMAC_DEF_TX_DESCS 512
|
||||
#define EMAC_DEF_RX_DESCS 256
|
||||
|
||||
#define EMAC_DEF_RX_IRQ_MOD 250
|
||||
#define EMAC_DEF_TX_IRQ_MOD 250
|
||||
|
||||
#define EMAC_WATCHDOG_TIME (5 * HZ)
|
||||
|
||||
/* by default check link every 4 seconds */
|
||||
#define EMAC_TRY_LINK_TIMEOUT (4 * HZ)
|
||||
|
||||
/* emac_irq per-device (per-adapter) irq properties.
|
||||
* @irq: irq number.
|
||||
* @mask mask to use over status register.
|
||||
*/
|
||||
struct emac_irq {
|
||||
unsigned int irq;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
/* The device's main data structure */
|
||||
struct emac_adapter {
|
||||
struct net_device *netdev;
|
||||
struct mii_bus *mii_bus;
|
||||
struct phy_device *phydev;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *csr;
|
||||
|
||||
struct emac_phy phy;
|
||||
struct emac_stats stats;
|
||||
|
||||
struct emac_irq irq;
|
||||
struct clk *clk[EMAC_CLK_CNT];
|
||||
|
||||
/* All Descriptor memory */
|
||||
struct emac_ring_header ring_header;
|
||||
struct emac_tx_queue tx_q;
|
||||
struct emac_rx_queue rx_q;
|
||||
unsigned int tx_desc_cnt;
|
||||
unsigned int rx_desc_cnt;
|
||||
unsigned int rrd_size; /* in quad words */
|
||||
unsigned int rfd_size; /* in quad words */
|
||||
unsigned int tpd_size; /* in quad words */
|
||||
|
||||
unsigned int rxbuf_size;
|
||||
|
||||
/* Ring parameter */
|
||||
u8 tpd_burst;
|
||||
u8 rfd_burst;
|
||||
unsigned int dmaw_dly_cnt;
|
||||
unsigned int dmar_dly_cnt;
|
||||
enum emac_dma_req_block dmar_block;
|
||||
enum emac_dma_req_block dmaw_block;
|
||||
enum emac_dma_order dma_order;
|
||||
|
||||
u32 irq_mod;
|
||||
u32 preamble;
|
||||
|
||||
struct work_struct work_thread;
|
||||
|
||||
u16 msg_enable;
|
||||
|
||||
struct mutex reset_lock;
|
||||
};
|
||||
|
||||
int emac_reinit_locked(struct emac_adapter *adpt);
|
||||
void emac_reg_update32(void __iomem *addr, u32 mask, u32 val);
|
||||
irqreturn_t emac_isr(int irq, void *data);
|
||||
|
||||
#endif /* _EMAC_H_ */
|
Загрузка…
Ссылка в новой задаче