Commit 1edb9ca6 authored by Siva Reddy's avatar Siva Reddy Committed by David S. Miller

net: sxgbe: add basic framework for Samsung 10Gb ethernet driver

This patch adds support for Samsung 10Gb ethernet driver(sxgbe).

- sxgbe core initialization
- Tx and Rx support
- MDIO support
- ISRs for Tx and Rx
- ifconfig support to driver
Signed-off-by: default avatarSiva Reddy Kallam <siva.kallam@samsung.com>
Signed-off-by: default avatarVipul Pandya <vipul.pandya@samsung.com>
Signed-off-by: default avatarGirish K S <ks.giri@samsung.com>
Neatening-by: default avatarJoe Perches <joe@perches.com>
Signed-off-by: default avatarByungho An <bh74.an@samsung.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5221d3e6
......@@ -150,6 +150,7 @@ config S6GMAC
To compile this driver as a module, choose M here. The module
will be called s6gmac.
source "drivers/net/ethernet/samsung/Kconfig"
source "drivers/net/ethernet/seeq/Kconfig"
source "drivers/net/ethernet/silan/Kconfig"
source "drivers/net/ethernet/sis/Kconfig"
......
......@@ -61,6 +61,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_S6GMAC) += s6gmac.o
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
obj-$(CONFIG_NET_VENDOR_SIS) += sis/
......
#
# Samsung Ethernet device configuration
#
config NET_VENDOR_SAMSUNG
bool "Samsung Ethernet device"
default y
---help---
This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
platforms.
if NET_VENDOR_SAMSUNG
source "drivers/net/ethernet/samsung/sxgbe/Kconfig"
endif # NET_VENDOR_SAMSUNG
#
# Makefile for the Samsung Ethernet device drivers.
#
obj-$(CONFIG_SXGBE_ETH) += sxgbe/
config SXGBE_ETH
tristate "Samsung 10G/2.5G/1G SXGBE Ethernet driver"
depends on HAS_IOMEM && HAS_DMA
select PHYLIB
select CRC32
select PTP_1588_CLOCK
---help---
This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
platforms.
obj-$(CONFIG_SXGBE_ETH) += samsung-sxgbe.o
samsung-sxgbe-objs:= sxgbe_platform.o sxgbe_main.o sxgbe_desc.o \
sxgbe_dma.o sxgbe_core.o sxgbe_mtl.o sxgbe_mdio.o \
sxgbe_ethtool.o sxgbe_xpcs.o $(samsung-sxgbe-y)
This diff is collapsed.
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/io.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
/* MAC core initialization */
static void sxgbe_core_init(void __iomem *ioaddr)
{
u32 regval;
/* TX configuration */
regval = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* Other configurable parameters IFP, IPG, ISR, ISM
* needs to be set if needed
*/
regval |= SXGBE_TX_JABBER_DISABLE;
writel(regval, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* RX configuration */
regval = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
/* Other configurable parameters CST, SPEN, USP, GPSLCE
* WD, LM, S2KP, HDSMS, GPSL, ELEN, ARPEN needs to be
* set if needed
*/
regval |= SXGBE_RX_JUMBPKT_ENABLE | SXGBE_RX_ACS_ENABLE;
writel(regval, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
}
/* Dump MAC registers */
static void sxgbe_core_dump_regs(void __iomem *ioaddr)
{
}
/* Handle extra events on specific interrupts hw dependent */
static int sxgbe_core_host_irq_status(void __iomem *ioaddr,
struct sxgbe_extra_stats *x)
{
return 0;
}
/* Set power management mode (e.g. magic frame) */
static void sxgbe_core_pmt(void __iomem *ioaddr, unsigned long mode)
{
}
/* Set/Get Unicast MAC addresses */
static void sxgbe_core_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
u32 high_word, low_word;
high_word = (addr[5] << 8) || (addr[4]);
low_word = ((addr[3] << 24) || (addr[2] << 16) ||
(addr[1] << 8) || (addr[0]));
writel(high_word, ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
writel(low_word, ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
}
static void sxgbe_core_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
u32 high_word, low_word;
high_word = readl(ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
low_word = readl(ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
/* extract and assign address */
addr[5] = (high_word & 0x0000FF00) >> 8;
addr[4] = (high_word & 0x000000FF);
addr[3] = (low_word & 0xFF000000) >> 24;
addr[2] = (low_word & 0x00FF0000) >> 16;
addr[1] = (low_word & 0x0000FF00) >> 8;
addr[0] = (low_word & 0x000000FF);
}
static void sxgbe_enable_tx(void __iomem *ioaddr, bool enable)
{
u32 tx_config;
tx_config = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
tx_config &= ~SXGBE_TX_ENABLE;
if (enable)
tx_config |= SXGBE_TX_ENABLE;
writel(tx_config, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
}
static void sxgbe_enable_rx(void __iomem *ioaddr, bool enable)
{
u32 rx_config;
rx_config = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
rx_config &= ~SXGBE_RX_ENABLE;
if (enable)
rx_config |= SXGBE_RX_ENABLE;
writel(rx_config, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
}
static int sxgbe_get_controller_version(void __iomem *ioaddr)
{
return readl(ioaddr + SXGBE_CORE_VERSION_REG);
}
/* If supported then get the optional core features */
static unsigned int sxgbe_get_hw_feature(void __iomem *ioaddr,
unsigned char feature_index)
{
return readl(ioaddr + (SXGBE_CORE_HW_FEA_REG(feature_index)));
}
static void sxgbe_core_set_speed(void __iomem *ioaddr, unsigned char speed)
{
u32 tx_cfg = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* clear the speed bits */
tx_cfg &= ~0x60000000;
tx_cfg |= (speed << SXGBE_SPEED_LSHIFT);
/* set the speed */
writel(tx_cfg, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
}
const struct sxgbe_core_ops core_ops = {
.core_init = sxgbe_core_init,
.dump_regs = sxgbe_core_dump_regs,
.host_irq_status = sxgbe_core_host_irq_status,
.pmt = sxgbe_core_pmt,
.set_umac_addr = sxgbe_core_set_umac_addr,
.get_umac_addr = sxgbe_core_get_umac_addr,
.enable_rx = sxgbe_enable_rx,
.enable_tx = sxgbe_enable_tx,
.get_controller_version = sxgbe_get_controller_version,
.get_hw_feature = sxgbe_get_hw_feature,
.set_speed = sxgbe_core_set_speed,
};
const struct sxgbe_core_ops *sxgbe_get_core_ops(void)
{
return &core_ops;
}
This diff is collapsed.
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_DESC_H__
#define __SXGBE_DESC_H__
#define SXGBE_DESC_SIZE_BYTES 16
/* forward declaration */
struct sxgbe_extra_stats;
/* Transmit checksum insertion control */
enum tdes_csum_insertion {
cic_disabled = 0, /* Checksum Insertion Control */
cic_only_ip = 1, /* Only IP header */
/* IP header but pseudoheader is not calculated */
cic_no_pseudoheader = 2,
cic_full = 3, /* IP header and pseudoheader */
};
struct sxgbe_tx_norm_desc {
u64 tdes01; /* buf1 address */
union {
/* TX Read-Format Desc 2,3 */
struct {
/* TDES2 */
u32 buf1_size:14;
u32 vlan_tag_ctl:2;
u32 buf2_size:14;
u32 timestmp_enable:1;
u32 int_on_com:1;
/* TDES3 */
union {
u32 tcp_payload_len:18;
struct {
u32 total_pkt_len:15;
u32 reserved1:1;
u32 cksum_ctl:2;
} cksum_pktlen;
} tx_pkt_len;
u32 tse_bit:1;
u32 tcp_hdr_len:4;
u32 sa_insert_ctl:3;
u32 crc_pad_ctl:2;
u32 last_desc:1;
u32 first_desc:1;
u32 ctxt_bit:1;
u32 own_bit:1;
} tx_rd_des23;
/* tx write back Desc 2,3 */
struct {
/* WB TES2 */
u32 reserved1;
/* WB TES3 */
u32 reserved2:31;
u32 own_bit:1;
} tx_wb_des23;
} tdes23;
};
struct sxgbe_rx_norm_desc {
union {
u32 rdes0; /* buf1 address */
struct {
u32 out_vlan_tag:16;
u32 in_vlan_tag:16;
} wb_rx_des0;
} rd_wb_des0;
union {
u32 rdes1; /* buf2 address or buf1[63:32] */
u32 rss_hash; /* Write-back RX */
} rd_wb_des1;
union {
/* RX Read format Desc 2,3 */
struct{
/* RDES2 */
u32 buf2_addr;
/* RDES3 */
u32 buf2_hi_addr:30;
u32 int_on_com:1;
u32 own_bit:1;
} rx_rd_des23;
/* RX write back */
struct{
/* WB RDES2 */
u32 hdr_len:10;
u32 rdes2_reserved:2;
u32 elrd_val:1;
u32 iovt_sel:1;
u32 res_pkt:1;
u32 vlan_filter_match:1;
u32 sa_filter_fail:1;
u32 da_filter_fail:1;
u32 hash_filter_pass:1;
u32 macaddr_filter_match:8;
u32 l3_filter_match:1;
u32 l4_filter_match:1;
u32 l34_filter_num:3;
/* WB RDES3 */
u32 pkt_len:14;
u32 rdes3_reserved:1;
u32 err_summary:15;
u32 err_l2_type:4;
u32 layer34_pkt_type:4;
u32 no_coagulation_pkt:1;
u32 in_seq_pkt:1;
u32 rss_valid:1;
u32 context_des_avail:1;
u32 last_desc:1;
u32 first_desc:1;
u32 recv_context_desc:1;
u32 own_bit:1;
} rx_wb_des23;
} rdes23;
};
/* Context descriptor structure */
struct sxgbe_tx_ctxt_desc {
u32 tstamp_lo;
u32 tstamp_hi;
u32 maxseg_size:15;
u32 reserved1:1;
u32 ivlan_tag:16;
u32 vlan_tag:16;
u32 vltag_valid:1;
u32 ivlan_tag_valid:1;
u32 ivlan_tag_ctl:2;
u32 reserved2:3;
u32 ctxt_desc_err:1;
u32 reserved3:2;
u32 ostc:1;
u32 tcmssv:1;
u32 reserved4:2;
u32 ctxt_bit:1;
u32 own_bit:1;
};
struct sxgbe_rx_ctxt_desc {
u32 tstamp_lo;
u32 tstamp_hi;
u32 reserved1;
u32 ptp_msgtype:4;
u32 tstamp_available:1;
u32 ptp_rsp_err:1;
u32 tstamp_dropped:1;
u32 reserved2:23;
u32 rx_ctxt_desc:1;
u32 own_bit:1;
};
struct sxgbe_desc_ops {
/* DMA TX descriptor ring initialization */
void (*init_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Invoked by the xmit function to prepare the tx descriptor */
void (*tx_desc_enable_tse)(struct sxgbe_tx_norm_desc *p, u8 is_tse,
u32 total_hdr_len, u32 payload_len,
u32 tcp_payload_len);
/* Assign buffer lengths for descriptor */
void (*prepare_tx_desc)(struct sxgbe_tx_norm_desc *p, u8 is_fd,
int buf1_len, int pkt_len, int cksum);
/* Set VLAN control information */
void (*tx_vlanctl_desc)(struct sxgbe_tx_norm_desc *p, int vlan_ctl);
/* Set the owner of the descriptor */
void (*set_tx_owner)(struct sxgbe_tx_norm_desc *p);
/* Get the owner of the descriptor */
int (*get_tx_owner)(struct sxgbe_tx_norm_desc *p);
/* Invoked by the xmit function to close the tx descriptor */
void (*close_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Clean the tx descriptor as soon as the tx irq is received */
void (*release_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted
*/
void (*clear_tx_ic)(struct sxgbe_tx_norm_desc *p);
/* Last tx segment reports the transmit status */
int (*get_tx_ls)(struct sxgbe_tx_norm_desc *p);
/* Get the buffer size from the descriptor */
int (*get_tx_len)(struct sxgbe_tx_norm_desc *p);
/* Set tx timestamp enable bit */
void (*tx_enable_tstamp)(struct sxgbe_tx_norm_desc *p);
/* get tx timestamp status */
int (*get_tx_timestamp_status)(struct sxgbe_tx_norm_desc *p);
/* TX Context Descripto Specific */
void (*tx_ctxt_desc_set_ctxt)(struct sxgbe_tx_ctxt_desc *p);
/* Set the owner of the TX context descriptor */
void (*tx_ctxt_desc_set_owner)(struct sxgbe_tx_ctxt_desc *p);
/* Get the owner of the TX context descriptor */
int (*get_tx_ctxt_owner)(struct sxgbe_tx_ctxt_desc *p);
/* Set TX mss */
void (*tx_ctxt_desc_set_mss)(struct sxgbe_tx_ctxt_desc *p, int mss);
/* Set TX mss */
int (*tx_ctxt_desc_get_mss)(struct sxgbe_tx_ctxt_desc *p);
/* Set TX tcmssv */
void (*tx_ctxt_desc_set_tcmssv)(struct sxgbe_tx_ctxt_desc *p);
/* Reset TX ostc */
void (*tx_ctxt_desc_reset_ostc)(struct sxgbe_tx_ctxt_desc *p);
/* Set IVLAN information */
void (*tx_ctxt_desc_set_ivlantag)(struct sxgbe_tx_ctxt_desc *p,
int is_ivlanvalid, int ivlan_tag,
int ivlan_ctl);
/* Return IVLAN Tag */
int (*tx_ctxt_desc_get_ivlantag)(struct sxgbe_tx_ctxt_desc *p);
/* Set VLAN Tag */
void (*tx_ctxt_desc_set_vlantag)(struct sxgbe_tx_ctxt_desc *p,
int is_vlanvalid, int vlan_tag);
/* Return VLAN Tag */
int (*tx_ctxt_desc_get_vlantag)(struct sxgbe_tx_ctxt_desc *p);
/* Set Time stamp */
void (*tx_ctxt_set_tstamp)(struct sxgbe_tx_ctxt_desc *p,
u8 ostc_enable, u64 tstamp);
/* Close TX context descriptor */
void (*close_tx_ctxt_desc)(struct sxgbe_tx_ctxt_desc *p);
/* WB status of context descriptor */
int (*get_tx_ctxt_cde)(struct sxgbe_tx_ctxt_desc *p);
/* DMA RX descriptor ring initialization */
void (*init_rx_desc)(struct sxgbe_rx_norm_desc *p, int disable_rx_ic,
int mode, int end);
/* Get own bit */
int (*get_rx_owner)(struct sxgbe_rx_norm_desc *p);
/* Set own bit */
void (*set_rx_owner)(struct sxgbe_rx_norm_desc *p);
/* Get the receive frame size */
int (*get_rx_frame_len)(struct sxgbe_rx_norm_desc *p);
/* Return first Descriptor status */
int (*get_rx_fd_status)(struct sxgbe_rx_norm_desc *p);
/* Return first Descriptor status */
int (*get_rx_ld_status)(struct sxgbe_rx_norm_desc *p);
/* Return the reception status looking at the RDES1 */
int (*rx_wbstatus)(struct sxgbe_rx_norm_desc *p,
struct sxgbe_extra_stats *x, int *checksum);
/* Get own bit */
int (*get_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
/* Set own bit */
void (*set_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
/* Return the reception status looking at Context control information */
void (*rx_ctxt_wbstatus)(struct sxgbe_rx_ctxt_desc *p,
struct sxgbe_extra_stats *x);
/* Get rx timestamp status */
int (*get_rx_ctxt_tstamp_status)(struct sxgbe_rx_ctxt_desc *p);
/* Get timestamp value for rx, need to check this */
u64 (*get_timestamp)(struct sxgbe_rx_ctxt_desc *p);
};
const struct sxgbe_desc_ops *sxgbe_get_desc_ops(void);
#endif /* __SXGBE_DESC_H__ */
This diff is collapsed.
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_DMA_H__
#define __SXGBE_DMA_H__
/* forward declaration */
struct sxgbe_extra_stats;
#define SXGBE_DMA_BLENMAP_LSHIFT 1
#define SXGBE_DMA_TXPBL_LSHIFT 16
#define SXGBE_DMA_RXPBL_LSHIFT 16
#define DEFAULT_DMA_PBL 8
struct sxgbe_dma_ops {
/* DMA core initialization */
int (*init)(void __iomem *ioaddr, int fix_burst, int burst_map);
void (*cha_init)(void __iomem *ioaddr, int cha_num, int fix_burst,
int pbl, dma_addr_t dma_tx, dma_addr_t dma_rx,
int t_rzie, int r_rsize);
void (*enable_dma_transmission)(void __iomem *ioaddr, int dma_cnum);
void (*enable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
void (*disable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
void (*start_tx)(void __iomem *ioaddr, int tchannels);
void (*start_tx_queue)(void __iomem *ioaddr, int dma_cnum);
void (*stop_tx)(void __iomem *ioaddr, int tchannels);
void (*stop_tx_queue)(void __iomem *ioaddr, int dma_cnum);
void (*start_rx)(void __iomem *ioaddr, int rchannels);
void (*stop_rx)(void __iomem *ioaddr, int rchannels);
int (*tx_dma_int_status)(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x);
int (*rx_dma_int_status)(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x);
/* Program the HW RX Watchdog */
void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt);
};
const struct sxgbe_dma_ops *sxgbe_get_dma_ops(void);
#endif /* __SXGBE_CORE_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
struct sxgbe_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
#define SXGBE_STAT(m) \
{ \
#m, \
FIELD_SIZEOF(struct sxgbe_extra_stats, m), \
offsetof(struct sxgbe_priv_data, xstats.m) \
}
static const struct sxgbe_stats sxgbe_gstrings_stats[] = {
};
#define SXGBE_STATS_LEN ARRAY_SIZE(sxgbe_gstrings_stats)
static const struct ethtool_ops sxgbe_ethtool_ops = {
};
void sxgbe_set_ethtool_ops(struct net_device *netdev)
{
SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops);
}
This diff is collapsed.
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/slab.h>
#include <linux/sxgbe_platform.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
#define SXGBE_SMA_WRITE_CMD 0x01 /* write command */
#define SXGBE_SMA_PREAD_CMD 0x02 /* post read increament address */
#define SXGBE_SMA_READ_CMD 0x03 /* read command */
#define SXGBE_SMA_SKIP_ADDRFRM 0x00040000 /* skip the address frame */
#define SXGBE_MII_BUSY 0x00800000 /* mii busy */
static int sxgbe_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_data)
{
unsigned long fin_time = jiffies + 3 * HZ; /* 3 seconds */
while (!time_after(jiffies, fin_time)) {
if (!(readl(ioaddr + mii_data) & SXGBE_MII_BUSY))
return 0;
cpu_relax();
}
return -EBUSY;
}
static void sxgbe_mdio_ctrl_data(struct sxgbe_priv_data *sp, u32 cmd,
u16 phydata)
{
u32 reg = phydata;
reg |= (cmd << 16) | SXGBE_SMA_SKIP_ADDRFRM |
((sp->clk_csr & 0x7) << 19) | SXGBE_MII_BUSY;
writel(reg, sp->ioaddr + sp->hw->mii.data);
}
static void sxgbe_mdio_c45(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
u32 reg;
/* set mdio address register */
reg = ((phyreg >> 16) & 0x1f) << 21;
reg |= (phyaddr << 16) | (phyreg & 0xffff);
writel(reg, sp->ioaddr + sp->hw->mii.addr);
sxgbe_mdio_ctrl_data(sp, cmd, phydata);
}
static void sxgbe_mdio_c22(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
u32 reg;
writel(1 << phyaddr, sp->ioaddr + SXGBE_MDIO_CLAUSE22_PORT_REG);
/* set mdio address register */
reg = (phyaddr << 16) | (phyreg & 0x1f);
writel(reg, sp->ioaddr + sp->hw->mii.addr);
sxgbe_mdio_ctrl_data(sp, cmd, phydata);
}
static int sxgbe_mdio_access(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
const struct mii_regs *mii = &sp->hw->mii;
int rc;
rc = sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
if (rc < 0)
return rc;
if (phyreg & MII_ADDR_C45) {
sxgbe_mdio_c45(sp, cmd, phyaddr, phyreg, phydata);
} else {
/* Ports 0-3 only support C22. */
if (phyaddr >= 4)
return -ENODEV;
sxgbe_mdio_c22(sp, cmd, phyaddr, phyreg, phydata);
}
return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
}
/**
* sxgbe_mdio_read
* @bus: points to the mii_bus structure
* @phyaddr: address of phy port
* @phyreg: address of register with in phy register
* Description: this function used for C45 and C22 MDIO Read
*/
static int sxgbe_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
{
struct net_device *ndev = bus->priv;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
int rc;
rc = sxgbe_mdio_access(priv, SXGBE_SMA_READ_CMD, phyaddr, phyreg, 0);
if (rc < 0)
return rc;
return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff;
}
/**
* sxgbe_mdio_write
* @bus: points to the mii_bus structure
* @phyaddr: address of phy port
* @phyreg: address of phy registers
* @phydata: data to be written into phy register
* Description: this function is used for C45 and C22 MDIO write
*/
static int sxgbe_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
u16 phydata)
{
struct net_device *ndev = bus->priv;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
return sxgbe_mdio_access(priv, SXGBE_SMA_WRITE_CMD, phyaddr, phyreg,
phydata);
}
int sxgbe_mdio_register(struct net_device *ndev)
{
struct mii_bus *mdio_bus;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
struct sxgbe_mdio_bus_data *mdio_data = priv->plat->mdio_bus_data;
int err, phy_addr;
int *irqlist;
bool act;
/* allocate the new mdio bus */
mdio_bus = mdiobus_alloc();
if (!mdio_bus) {
netdev_err(ndev, "%s: mii bus allocation failed\n", __func__);
return -ENOMEM;
}
if (mdio_data->irqs)
irqlist = mdio_data->irqs;
else
irqlist = priv->mii_irq;
/* assign mii bus fields */
mdio_bus->name = "samsxgbe";
mdio_bus->read = &sxgbe_mdio_read;
mdio_bus->write = &sxgbe_mdio_write;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x",
mdio_bus->name, priv->plat->bus_id);
mdio_bus->priv = ndev;
mdio_bus->phy_mask = mdio_data->phy_mask;
mdio_bus->parent = priv->device;
/* register with kernel subsystem */
err = mdiobus_register(mdio_bus);
if (err != 0) {
netdev_err(ndev, "mdiobus register failed\n");
goto mdiobus_err;
}
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
struct phy_device *phy = mdio_bus->phy_map[phy_addr];
if (phy) {
char irq_num[4];
char *irq_str;
/* If an IRQ was provided to be assigned after
* the bus probe, do it here.
*/
if ((mdio_data->irqs == NULL) &&
(mdio_data->probed_phy_irq > 0)) {
irqlist[phy_addr] = mdio_data->probed_phy_irq;
phy->irq = mdio_data->probed_phy_irq;
}
/* If we're going to bind the MAC to this PHY bus,
* and no PHY number was provided to the MAC,
* use the one probed here.
*/
if (priv->plat->phy_addr == -1)
priv->plat->phy_addr = phy_addr;
act = (priv->plat->phy_addr == phy_addr);
switch (phy->irq) {
case PHY_POLL:
irq_str = "POLL";
break;
case PHY_IGNORE_INTERRUPT:
irq_str = "IGNORE";
break;
default:
sprintf(irq_num, "%d", phy->irq);
irq_str = irq_num;
break;
}
netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n",
phy->phy_id, phy_addr, irq_str,
dev_name(&phy->dev), act ? " active" : "");
}
}
if (!err) {
netdev_err(ndev, "PHY not found\n");
mdiobus_unregister(mdio_bus);
mdiobus_free(mdio_bus);
goto mdiobus_err;
}
priv->mii = mdio_bus;
return 0;
mdiobus_err:
mdiobus_free(mdio_bus);
return err;
}
int sxgbe_mdio_unregister(struct net_device *ndev)
{
struct sxgbe_priv_data *priv = netdev_priv(ndev);
if (!priv->mii)
return 0;
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
mdiobus_free(priv->mii);
priv->mii = NULL;
return 0;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/jiffies.h>
#include "sxgbe_mtl.h"
#include "sxgbe_reg.h"
static void sxgbe_mtl_init(void __iomem *ioaddr, unsigned int etsalg,
unsigned int raa)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_OP_MODE_REG);
reg_val &= ETS_RST;
/* ETS Algorith */
switch (etsalg & SXGBE_MTL_OPMODE_ESTMASK) {
case ETS_WRR:
reg_val &= ETS_WRR;
break;
case ETS_WFQ:
reg_val |= ETS_WFQ;
break;
case ETS_DWRR:
reg_val |= ETS_DWRR;
break;
}
writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
switch (raa & SXGBE_MTL_OPMODE_RAAMASK) {
case RAA_SP:
reg_val &= RAA_SP;
break;
case RAA_WSP:
reg_val |= RAA_WSP;
break;
}
writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
}
/* For Dynamic DMA channel mapping for Rx queue */
static void sxgbe_mtl_dma_dm_rxqueue(void __iomem *ioaddr)
{
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP0_REG);
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP1_REG);
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP2_REG);
}
static void sxgbe_mtl_set_txfifosize(void __iomem *ioaddr, int queue_num,
int queue_fifo)
{
u32 fifo_bits, reg_val;
/* 0 means 256 bytes */
fifo_bits = (queue_fifo / SXGBE_MTL_TX_FIFO_DIV) - 1;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_set_rxfifosize(void __iomem *ioaddr, int queue_num,
int queue_fifo)
{
u32 fifo_bits, reg_val;
/* 0 means 256 bytes */
fifo_bits = (queue_fifo / SXGBE_MTL_RX_FIFO_DIV)-1;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_enable_txqueue(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_ENABLE_QUEUE;
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_disable_txqueue(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val &= ~SXGBE_MTL_ENABLE_QUEUE;
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_active(void __iomem *ioaddr, int queue_num,
int threshold)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_ACTIVE);
reg_val |= (threshold << RX_FC_ACTIVE);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_ENABLE_FC;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_deactive(void __iomem *ioaddr, int queue_num,
int threshold)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_DEACTIVE);
reg_val |= (threshold << RX_FC_DEACTIVE);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fep_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_RXQ_OP_FEP;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fep_disable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_RXQ_OP_FEP);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fup_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_RXQ_OP_FUP;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fup_disable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_RXQ_OP_FUP);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_set_tx_mtl_mode(void __iomem *ioaddr, int queue_num,
int tx_mode)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
/* TX specific MTL mode settings */
if (tx_mode == SXGBE_MTL_SFMODE) {
reg_val |= SXGBE_MTL_SFMODE;
} else {
/* set the TTC values */
if (tx_mode <= 64)
reg_val |= MTL_CONTROL_TTC_64;
else if (tx_mode <= 96)
reg_val |= MTL_CONTROL_TTC_96;
else if (tx_mode <= 128)
reg_val |= MTL_CONTROL_TTC_128;
else if (tx_mode <= 192)
reg_val |= MTL_CONTROL_TTC_192;
else if (tx_mode <= 256)
reg_val |= MTL_CONTROL_TTC_256;
else if (tx_mode <= 384)
reg_val |= MTL_CONTROL_TTC_384;
else
reg_val |= MTL_CONTROL_TTC_512;
}
/* write into TXQ operation register */
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_set_rx_mtl_mode(void __iomem *ioaddr, int queue_num,
int rx_mode)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
/* RX specific MTL mode settings */
if (rx_mode == SXGBE_RX_MTL_SFMODE) {
reg_val |= SXGBE_RX_MTL_SFMODE;
} else {
if (rx_mode <= 64)
reg_val |= MTL_CONTROL_RTC_64;
else if (rx_mode <= 96)
reg_val |= MTL_CONTROL_RTC_96;
else if (rx_mode <= 128)
reg_val |= MTL_CONTROL_RTC_128;
}
/* write into RXQ operation register */
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static const struct sxgbe_mtl_ops mtl_ops = {
.mtl_set_txfifosize = sxgbe_mtl_set_txfifosize,
.mtl_set_rxfifosize = sxgbe_mtl_set_rxfifosize,
.mtl_enable_txqueue = sxgbe_mtl_enable_txqueue,
.mtl_disable_txqueue = sxgbe_mtl_disable_txqueue,
.mtl_dynamic_dma_rxqueue = sxgbe_mtl_dma_dm_rxqueue,
.set_tx_mtl_mode = sxgbe_set_tx_mtl_mode,
.set_rx_mtl_mode = sxgbe_set_rx_mtl_mode,
.mtl_init = sxgbe_mtl_init,
.mtl_fc_active = sxgbe_mtl_fc_active,
.mtl_fc_deactive = sxgbe_mtl_fc_deactive,
.mtl_fc_enable = sxgbe_mtl_fc_enable,
.mtl_fep_enable = sxgbe_mtl_fep_enable,
.mtl_fep_disable = sxgbe_mtl_fep_disable,
.mtl_fup_enable = sxgbe_mtl_fup_enable,
.mtl_fup_disable = sxgbe_mtl_fup_disable
};
const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void)
{
return &mtl_ops;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_MTL_H__
#define __SXGBE_MTL_H__
#define SXGBE_MTL_OPMODE_ESTMASK 0x3
#define SXGBE_MTL_OPMODE_RAAMASK 0x1
#define SXGBE_MTL_FCMASK 0x7
#define SXGBE_MTL_TX_FIFO_DIV 256
#define SXGBE_MTL_RX_FIFO_DIV 256
#define SXGBE_MTL_RXQ_OP_FEP BIT(4)
#define SXGBE_MTL_RXQ_OP_FUP BIT(3)
#define SXGBE_MTL_ENABLE_FC 0x80
#define ETS_WRR 0xFFFFFF9F
#define ETS_RST 0xFFFFFF9F
#define ETS_WFQ 0x00000020
#define ETS_DWRR 0x00000040
#define RAA_SP 0xFFFFFFFB
#define RAA_WSP 0x00000004
#define RX_QUEUE_DYNAMIC 0x80808080
#define RX_FC_ACTIVE 8
#define RX_FC_DEACTIVE 13
enum ttc_control {
MTL_CONTROL_TTC_64 = 0x00000000,
MTL_CONTROL_TTC_96 = 0x00000020,
MTL_CONTROL_TTC_128 = 0x00000030,
MTL_CONTROL_TTC_192 = 0x00000040,
MTL_CONTROL_TTC_256 = 0x00000050,
MTL_CONTROL_TTC_384 = 0x00000060,
MTL_CONTROL_TTC_512 = 0x00000070,
};
enum rtc_control {
MTL_CONTROL_RTC_64 = 0x00000000,
MTL_CONTROL_RTC_96 = 0x00000002,
MTL_CONTROL_RTC_128 = 0x00000003,
};
enum flow_control_th {
MTL_FC_FULL_1K = 0x00000000,
MTL_FC_FULL_2K = 0x00000001,
MTL_FC_FULL_4K = 0x00000002,
MTL_FC_FULL_5K = 0x00000003,
MTL_FC_FULL_6K = 0x00000004,
MTL_FC_FULL_8K = 0x00000005,
MTL_FC_FULL_16K = 0x00000006,
MTL_FC_FULL_24K = 0x00000007,
};
struct sxgbe_mtl_ops {
void (*mtl_init)(void __iomem *ioaddr, unsigned int etsalg,
unsigned int raa);
void (*mtl_set_txfifosize)(void __iomem *ioaddr, int queue_num,
int mtl_fifo);
void (*mtl_set_rxfifosize)(void __iomem *ioaddr, int queue_num,
int queue_fifo);
void (*mtl_enable_txqueue)(void __iomem *ioaddr, int queue_num);
void (*mtl_disable_txqueue)(void __iomem *ioaddr, int queue_num);
void (*set_tx_mtl_mode)(void __iomem *ioaddr, int queue_num,
int tx_mode);
void (*set_rx_mtl_mode)(void __iomem *ioaddr, int queue_num,
int rx_mode);
void (*mtl_dynamic_dma_rxqueue)(void __iomem *ioaddr);
void (*mtl_fc_active)(void __iomem *ioaddr, int queue_num,
int threshold);
void (*mtl_fc_deactive)(void __iomem *ioaddr, int queue_num,
int threshold);
void (*mtl_fc_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fep_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fep_disable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fup_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fup_disable)(void __iomem *ioaddr, int queue_num);
};
const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void);
#endif /* __SXGBE_MTL_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/etherdevice.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/sxgbe_platform.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
#ifdef CONFIG_OF
static int sxgbe_probe_config_dt(struct platform_device *pdev,
struct sxgbe_plat_data *plat,
const char **mac)
{
struct device_node *np = pdev->dev.of_node;
struct sxgbe_dma_cfg *dma_cfg;
if (!np)
return -ENODEV;
*mac = of_get_mac_address(np);
plat->interface = of_get_phy_mode(np);
plat->bus_id = of_alias_get_id(np, "ethernet");
if (plat->bus_id < 0)
plat->bus_id = 0;
plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
sizeof(*plat->mdio_bus_data),
GFP_KERNEL);
dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL);
if (!dma_cfg)
return -ENOMEM;
plat->dma_cfg = dma_cfg;
of_property_read_u32(np, "samsung,pbl", &dma_cfg->pbl);
if (of_property_read_u32(np, "samsung,burst-map", &dma_cfg->burst_map) == 0)
dma_cfg->fixed_burst = true;
return 0;
}
#else
static int sxgbe_probe_config_dt(struct platform_device *pdev,
struct sxgbe_plat_data *plat,
const char **mac)
{
return -ENOSYS;
}
#endif /* CONFIG_OF */
/**
* sxgbe_platform_probe
* @pdev: platform device pointer
* Description: platform_device probe function. It allocates
* the necessary resources and invokes the main to init
* the net device, register the mdio bus etc.
*/
static int sxgbe_platform_probe(struct platform_device *pdev)
{
int ret;
int i, chan;
struct resource *res;
struct device *dev = &pdev->dev;
void __iomem *addr;
struct sxgbe_priv_data *priv = NULL;
struct sxgbe_plat_data *plat_dat = NULL;
const char *mac = NULL;
struct net_device *ndev = platform_get_drvdata(pdev);
struct device_node *node = dev->of_node;
/* Get memory resource */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto err_out;
addr = devm_ioremap_resource(dev, res);
if (IS_ERR(addr))
return PTR_ERR(addr);
if (pdev->dev.of_node) {
plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct sxgbe_plat_data),
GFP_KERNEL);
if (!plat_dat)
return -ENOMEM;
ret = sxgbe_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed\n", __func__);
return ret;
}
}
/* Get MAC address if available (DT) */
if (mac)
ether_addr_copy(priv->dev->dev_addr, mac);
priv = sxgbe_drv_probe(&(pdev->dev), plat_dat, addr);
if (!priv) {
pr_err("%s: main driver probe failed\n", __func__);
goto err_out;
}
/* Get the SXGBE common INT information */
priv->irq = irq_of_parse_and_map(node, 0);
if (priv->irq <= 0) {
dev_err(dev, "sxgbe common irq parsing failed\n");
goto err_drv_remove;
}
/* Get the TX/RX IRQ numbers */
for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) {
priv->txq[i]->irq_no = irq_of_parse_and_map(node, chan++);
if (priv->txq[i]->irq_no <= 0) {
dev_err(dev, "sxgbe tx irq parsing failed\n");
goto err_tx_irq_unmap;
}
}
for (i = 0; i < SXGBE_RX_QUEUES; i++) {
priv->rxq[i]->irq_no = irq_of_parse_and_map(node, chan++);
if (priv->rxq[i]->irq_no <= 0) {
dev_err(dev, "sxgbe rx irq parsing failed\n");
goto err_rx_irq_unmap;
}
}
platform_set_drvdata(pdev, priv->dev);
pr_debug("platform driver registration completed\n");
return 0;
err_rx_irq_unmap:
while (--i)
irq_dispose_mapping(priv->rxq[i]->irq_no);
i = SXGBE_TX_QUEUES;
err_tx_irq_unmap:
while (--i)
irq_dispose_mapping(priv->txq[i]->irq_no);
irq_dispose_mapping(priv->irq);
err_drv_remove:
sxgbe_drv_remove(ndev);
err_out:
return -ENODEV;
}
/**
* sxgbe_platform_remove
* @pdev: platform device pointer
* Description: this function calls the main to free the net resources
* and calls the platforms hook and release the resources (e.g. mem).
*/
static int sxgbe_platform_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
int ret = sxgbe_drv_remove(ndev);
return ret;
}
#ifdef CONFIG_PM
static int sxgbe_platform_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_suspend(ndev);
}
static int sxgbe_platform_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_resume(ndev);
}
int sxgbe_platform_freeze(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_freeze(ndev);
}
int sxgbe_platform_restore(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_restore(ndev);
}
static const struct dev_pm_ops sxgbe_platform_pm_ops = {
.suspend = sxgbe_platform_suspend,
.resume = sxgbe_platform_resume,
.freeze = sxgbe_platform_freeze,
.thaw = sxgbe_platform_restore,
.restore = sxgbe_platform_restore,
};
#else
static const struct dev_pm_ops sxgbe_platform_pm_ops;
#endif /* CONFIG_PM */
static const struct of_device_id sxgbe_dt_ids[] = {
{ .compatible = "samsung,sxgbe-v2.0a"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sxgbe_dt_ids);
struct platform_driver sxgbe_platform_driver = {
.probe = sxgbe_platform_probe,
.remove = sxgbe_platform_remove,
.driver = {
.name = SXGBE_RESOURCE_NAME,
.owner = THIS_MODULE,
.pm = &sxgbe_platform_pm_ops,
.of_match_table = of_match_ptr(sxgbe_dt_ids),
},
};
int sxgbe_register_platform(void)
{
int err;
err = platform_driver_register(&sxgbe_platform_driver);
if (err)
pr_err("failed to register the platform driver\n");
return err;
}
void sxgbe_unregister_platform(void)
{
platform_driver_unregister(&sxgbe_platform_driver);
}
This diff is collapsed.
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_xpcs.h"
static int sxgbe_xpcs_read(struct net_device *ndev, unsigned int reg)
{
u32 value;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
value = readl(priv->ioaddr + XPCS_OFFSET + reg);
return value;
}
static int sxgbe_xpcs_write(struct net_device *ndev, int reg, int data)
{
struct sxgbe_priv_data *priv = netdev_priv(ndev);
writel(data, priv->ioaddr + XPCS_OFFSET + reg);
return 0;
}
int sxgbe_xpcs_init(struct net_device *ndev)
{
u32 value;
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
/* 10G XAUI mode */
sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, value | BIT(13));
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) == XPCS_QSEQ_STATE_STABLE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
return 0;
}
int sxgbe_xpcs_init_1G(struct net_device *ndev)
{
int value;
/* 10GBASE-X PCS (1G) mode */
sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(13));
value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(6));
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value & ~BIT(13));
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
/* Auto Negotiation cluase 37 enable */
value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(12));
return 0;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Byungho An <bh74.an@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_XPCS_H__
#define __SXGBE_XPCS_H__
/* XPCS Registers */
#define XPCS_OFFSET 0x1A060000
#define SR_PCS_MMD_CONTROL1 0x030000
#define SR_PCS_CONTROL2 0x030007
#define VR_PCS_MMD_XAUI_MODE_CONTROL 0x038004
#define VR_PCS_MMD_DIGITAL_STATUS 0x038010
#define SR_MII_MMD_CONTROL 0x1F0000
#define SR_MII_MMD_AN_ADV 0x1F0004
#define SR_MII_MMD_AN_LINK_PARTNER_BA 0x1F0005
#define VR_MII_MMD_AN_CONTROL 0x1F8001
#define VR_MII_MMD_AN_INT_STATUS 0x1F8002
#define XPCS_QSEQ_STATE_STABLE 0x10
#define XPCS_QSEQ_STATE_MPLLOFF 0x1c
#define XPCS_TYPE_SEL_R 0x00
#define XPCS_TYPE_SEL_X 0x01
#define XPCS_TYPE_SEL_W 0x02
#define XPCS_XAUI_MODE 0x00
#define XPCS_RXAUI_MODE 0x01
int sxgbe_xpcs_init(struct net_device *ndev);
int sxgbe_xpcs_init_1G(struct net_device *ndev);
#endif /* __SXGBE_XPCS_H__ */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment