mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
107
drivers/net/ethernet/ti/Kconfig
Normal file
107
drivers/net/ethernet/ti/Kconfig
Normal file
|
@ -0,0 +1,107 @@
|
|||
#
|
||||
# TI device configuration
|
||||
#
|
||||
|
||||
config NET_VENDOR_TI
|
||||
bool "Texas Instruments (TI) devices"
|
||||
default y
|
||||
depends on PCI || EISA || AR7 || (ARM && (ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX || ARCH_KEYSTONE))
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, say Y
|
||||
and read the Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
Note that the answer to this question doesn't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all
|
||||
the questions about TI devices. If you say Y, you will be asked for
|
||||
your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_TI
|
||||
|
||||
config TI_DAVINCI_EMAC
|
||||
tristate "TI DaVinci EMAC Support"
|
||||
depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 )
|
||||
select TI_DAVINCI_MDIO
|
||||
select TI_DAVINCI_CPDMA
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports TI's DaVinci Ethernet .
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called davinci_emac_driver. This is recommended.
|
||||
|
||||
config TI_DAVINCI_MDIO
|
||||
tristate "TI DaVinci MDIO Support"
|
||||
depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX || ARCH_KEYSTONE )
|
||||
select PHYLIB
|
||||
---help---
|
||||
This driver supports TI's DaVinci MDIO module.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called davinci_mdio. This is recommended.
|
||||
|
||||
config TI_DAVINCI_CPDMA
|
||||
tristate "TI DaVinci CPDMA Support"
|
||||
depends on ARM && ( ARCH_DAVINCI || ARCH_OMAP3 || SOC_AM33XX )
|
||||
---help---
|
||||
This driver supports TI's DaVinci CPDMA dma engine.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called davinci_cpdma. This is recommended.
|
||||
|
||||
config TI_CPSW_PHY_SEL
|
||||
boolean "TI CPSW Switch Phy sel Support"
|
||||
depends on TI_CPSW
|
||||
---help---
|
||||
This driver supports configuring of the phy mode connected to
|
||||
the CPSW.
|
||||
|
||||
config TI_CPSW
|
||||
tristate "TI CPSW Switch Support"
|
||||
depends on ARM && (ARCH_DAVINCI || SOC_AM33XX)
|
||||
select TI_DAVINCI_CPDMA
|
||||
select TI_DAVINCI_MDIO
|
||||
select TI_CPSW_PHY_SEL
|
||||
select MFD_SYSCON
|
||||
select REGMAP
|
||||
---help---
|
||||
This driver supports TI's CPSW Ethernet Switch.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called cpsw.
|
||||
|
||||
config TI_CPTS
|
||||
boolean "TI Common Platform Time Sync (CPTS) Support"
|
||||
depends on TI_CPSW
|
||||
select PTP_1588_CLOCK
|
||||
---help---
|
||||
This driver supports the Common Platform Time Sync unit of
|
||||
the CPSW Ethernet Switch. The unit can time stamp PTP UDP/IPv4
|
||||
and Layer 2 packets, and the driver offers a PTP Hardware Clock.
|
||||
|
||||
config TLAN
|
||||
tristate "TI ThunderLAN support"
|
||||
depends on (PCI || EISA)
|
||||
---help---
|
||||
If you have a PCI Ethernet network card based on the ThunderLAN chip
|
||||
which is supported by this driver, say Y and read the
|
||||
Ethernet-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>.
|
||||
|
||||
Devices currently supported by this driver are Compaq Netelligent,
|
||||
Compaq NetFlex and Olicom cards. Please read the file
|
||||
<file:Documentation/networking/tlan.txt> for more details.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called tlan.
|
||||
|
||||
Please email feedback to <torben.mathiasen@compaq.com>.
|
||||
|
||||
config CPMAC
|
||||
tristate "TI AR7 CPMAC Ethernet support"
|
||||
depends on AR7
|
||||
select PHYLIB
|
||||
---help---
|
||||
TI AR7 CPMAC Ethernet support
|
||||
|
||||
endif # NET_VENDOR_TI
|
12
drivers/net/ethernet/ti/Makefile
Normal file
12
drivers/net/ethernet/ti/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for the TI network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TLAN) += tlan.o
|
||||
obj-$(CONFIG_CPMAC) += cpmac.o
|
||||
obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o
|
||||
obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
|
||||
obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
|
||||
obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o
|
||||
obj-$(CONFIG_TI_CPSW) += ti_cpsw.o
|
||||
ti_cpsw-y := cpsw_ale.o cpsw.o cpts.o
|
1295
drivers/net/ethernet/ti/cpmac.c
Normal file
1295
drivers/net/ethernet/ti/cpmac.c
Normal file
File diff suppressed because it is too large
Load diff
220
drivers/net/ethernet/ti/cpsw-phy-sel.c
Normal file
220
drivers/net/ethernet/ti/cpsw-phy-sel.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/* Texas Instruments Ethernet Switch Driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "cpsw.h"
|
||||
|
||||
/* AM33xx SoC specific definitions for the CONTROL port */
|
||||
#define AM33XX_GMII_SEL_MODE_MII 0
|
||||
#define AM33XX_GMII_SEL_MODE_RMII 1
|
||||
#define AM33XX_GMII_SEL_MODE_RGMII 2
|
||||
|
||||
#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
|
||||
#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
|
||||
|
||||
#define GMII_SEL_MODE_MASK 0x3
|
||||
|
||||
struct cpsw_phy_sel_priv {
|
||||
struct device *dev;
|
||||
u32 __iomem *gmii_sel;
|
||||
bool rmii_clock_external;
|
||||
void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
|
||||
phy_interface_t phy_mode, int slave);
|
||||
};
|
||||
|
||||
|
||||
static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
|
||||
phy_interface_t phy_mode, int slave)
|
||||
{
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 mode = 0;
|
||||
|
||||
reg = readl(priv->gmii_sel);
|
||||
|
||||
switch (phy_mode) {
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
mode = AM33XX_GMII_SEL_MODE_RMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||
mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_MII:
|
||||
default:
|
||||
mode = AM33XX_GMII_SEL_MODE_MII;
|
||||
break;
|
||||
};
|
||||
|
||||
mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
|
||||
mode <<= slave * 2;
|
||||
|
||||
if (priv->rmii_clock_external) {
|
||||
if (slave == 0)
|
||||
mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
|
||||
else
|
||||
mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
|
||||
}
|
||||
|
||||
reg &= ~mask;
|
||||
reg |= mode;
|
||||
|
||||
writel(reg, priv->gmii_sel);
|
||||
}
|
||||
|
||||
static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
|
||||
phy_interface_t phy_mode, int slave)
|
||||
{
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 mode = 0;
|
||||
|
||||
reg = readl(priv->gmii_sel);
|
||||
|
||||
switch (phy_mode) {
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
mode = AM33XX_GMII_SEL_MODE_RMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||
mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_MII:
|
||||
default:
|
||||
mode = AM33XX_GMII_SEL_MODE_MII;
|
||||
break;
|
||||
};
|
||||
|
||||
switch (slave) {
|
||||
case 0:
|
||||
mask = GMII_SEL_MODE_MASK;
|
||||
break;
|
||||
case 1:
|
||||
mask = GMII_SEL_MODE_MASK << 4;
|
||||
mode <<= 4;
|
||||
break;
|
||||
default:
|
||||
dev_err(priv->dev, "invalid slave number...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->rmii_clock_external)
|
||||
dev_err(priv->dev, "RMII External clock is not supported\n");
|
||||
|
||||
reg &= ~mask;
|
||||
reg |= mode;
|
||||
|
||||
writel(reg, priv->gmii_sel);
|
||||
}
|
||||
|
||||
static struct platform_driver cpsw_phy_sel_driver;
|
||||
static int match(struct device *dev, void *data)
|
||||
{
|
||||
struct device_node *node = (struct device_node *)data;
|
||||
return dev->of_node == node &&
|
||||
dev->driver == &cpsw_phy_sel_driver.driver;
|
||||
}
|
||||
|
||||
void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct cpsw_phy_sel_priv *priv;
|
||||
|
||||
node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
|
||||
if (!node) {
|
||||
dev_err(dev, "Phy mode driver DT not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev = bus_find_device(&platform_bus_type, NULL, node, match);
|
||||
priv = dev_get_drvdata(dev);
|
||||
|
||||
priv->cpsw_phy_sel(priv, phy_mode, slave);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpsw_phy_sel);
|
||||
|
||||
static const struct of_device_id cpsw_phy_sel_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,am3352-cpsw-phy-sel",
|
||||
.data = &cpsw_gmii_sel_am3352,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra7xx-cpsw-phy-sel",
|
||||
.data = &cpsw_gmii_sel_dra7xx,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am43xx-cpsw-phy-sel",
|
||||
.data = &cpsw_gmii_sel_am3352,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table);
|
||||
|
||||
static int cpsw_phy_sel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
struct cpsw_phy_sel_priv *priv;
|
||||
|
||||
of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->cpsw_phy_sel = of_id->data;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
|
||||
priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->gmii_sel))
|
||||
return PTR_ERR(priv->gmii_sel);
|
||||
|
||||
if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL))
|
||||
priv->rmii_clock_external = true;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cpsw_phy_sel_driver = {
|
||||
.probe = cpsw_phy_sel_probe,
|
||||
.driver = {
|
||||
.name = "cpsw-phy-sel",
|
||||
.of_match_table = cpsw_phy_sel_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(cpsw_phy_sel_driver);
|
||||
MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
2556
drivers/net/ethernet/ti/cpsw.c
Normal file
2556
drivers/net/ethernet/ti/cpsw.c
Normal file
File diff suppressed because it is too large
Load diff
45
drivers/net/ethernet/ti/cpsw.h
Normal file
45
drivers/net/ethernet/ti/cpsw.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* Texas Instruments Ethernet Switch Driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __CPSW_H__
|
||||
#define __CPSW_H__
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
struct cpsw_slave_data {
|
||||
char phy_id[MII_BUS_ID_SIZE];
|
||||
int phy_if;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */
|
||||
};
|
||||
|
||||
struct cpsw_platform_data {
|
||||
struct cpsw_slave_data *slave_data;
|
||||
u32 ss_reg_ofs; /* Subsystem control register offset */
|
||||
u32 channels; /* number of cpdma channels (symmetric) */
|
||||
u32 slaves; /* number of slave cpgmac ports */
|
||||
u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */
|
||||
u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */
|
||||
u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */
|
||||
u32 ale_entries; /* ale table size */
|
||||
u32 bd_ram_size; /*buffer descriptor ram size */
|
||||
u32 rx_descs; /* Number of Rx Descriptios */
|
||||
u32 mac_control; /* Mac control register */
|
||||
u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/
|
||||
bool dual_emac; /* Enable Dual EMAC mode */
|
||||
};
|
||||
|
||||
void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave);
|
||||
|
||||
#endif /* __CPSW_H__ */
|
809
drivers/net/ethernet/ti/cpsw_ale.c
Normal file
809
drivers/net/ethernet/ti/cpsw_ale.c
Normal file
|
@ -0,0 +1,809 @@
|
|||
/*
|
||||
* Texas Instruments 3-Port Ethernet Switch Address Lookup Engine
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "cpsw_ale.h"
|
||||
|
||||
#define BITMASK(bits) (BIT(bits) - 1)
|
||||
|
||||
#define ALE_VERSION_MAJOR(rev) ((rev >> 8) & 0xff)
|
||||
#define ALE_VERSION_MINOR(rev) (rev & 0xff)
|
||||
|
||||
/* ALE Registers */
|
||||
#define ALE_IDVER 0x00
|
||||
#define ALE_CONTROL 0x08
|
||||
#define ALE_PRESCALE 0x10
|
||||
#define ALE_UNKNOWNVLAN 0x18
|
||||
#define ALE_TABLE_CONTROL 0x20
|
||||
#define ALE_TABLE 0x34
|
||||
#define ALE_PORTCTL 0x40
|
||||
|
||||
#define ALE_TABLE_WRITE BIT(31)
|
||||
|
||||
#define ALE_TYPE_FREE 0
|
||||
#define ALE_TYPE_ADDR 1
|
||||
#define ALE_TYPE_VLAN 2
|
||||
#define ALE_TYPE_VLAN_ADDR 3
|
||||
|
||||
#define ALE_UCAST_PERSISTANT 0
|
||||
#define ALE_UCAST_UNTOUCHED 1
|
||||
#define ALE_UCAST_OUI 2
|
||||
#define ALE_UCAST_TOUCHED 3
|
||||
|
||||
static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = start / 32;
|
||||
start -= idx * 32;
|
||||
idx = 2 - idx; /* flip */
|
||||
return (ale_entry[idx] >> start) & BITMASK(bits);
|
||||
}
|
||||
|
||||
static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
|
||||
u32 value)
|
||||
{
|
||||
int idx;
|
||||
|
||||
value &= BITMASK(bits);
|
||||
idx = start / 32;
|
||||
start -= idx * 32;
|
||||
idx = 2 - idx; /* flip */
|
||||
ale_entry[idx] &= ~(BITMASK(bits) << start);
|
||||
ale_entry[idx] |= (value << start);
|
||||
}
|
||||
|
||||
#define DEFINE_ALE_FIELD(name, start, bits) \
|
||||
static inline int cpsw_ale_get_##name(u32 *ale_entry) \
|
||||
{ \
|
||||
return cpsw_ale_get_field(ale_entry, start, bits); \
|
||||
} \
|
||||
static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \
|
||||
{ \
|
||||
cpsw_ale_set_field(ale_entry, start, bits, value); \
|
||||
}
|
||||
|
||||
DEFINE_ALE_FIELD(entry_type, 60, 2)
|
||||
DEFINE_ALE_FIELD(vlan_id, 48, 12)
|
||||
DEFINE_ALE_FIELD(mcast_state, 62, 2)
|
||||
DEFINE_ALE_FIELD(port_mask, 66, 3)
|
||||
DEFINE_ALE_FIELD(super, 65, 1)
|
||||
DEFINE_ALE_FIELD(ucast_type, 62, 2)
|
||||
DEFINE_ALE_FIELD(port_num, 66, 2)
|
||||
DEFINE_ALE_FIELD(blocked, 65, 1)
|
||||
DEFINE_ALE_FIELD(secure, 64, 1)
|
||||
DEFINE_ALE_FIELD(vlan_untag_force, 24, 3)
|
||||
DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3)
|
||||
DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3)
|
||||
DEFINE_ALE_FIELD(vlan_member_list, 0, 3)
|
||||
DEFINE_ALE_FIELD(mcast, 40, 1)
|
||||
|
||||
/* The MAC address field in the ALE entry cannot be macroized as above */
|
||||
static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
|
||||
}
|
||||
|
||||
static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
|
||||
}
|
||||
|
||||
static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry)
|
||||
{
|
||||
int i;
|
||||
|
||||
WARN_ON(idx > ale->params.ale_entries);
|
||||
|
||||
__raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL);
|
||||
|
||||
for (i = 0; i < ALE_ENTRY_WORDS; i++)
|
||||
ale_entry[i] = __raw_readl(ale->params.ale_regs +
|
||||
ALE_TABLE + 4 * i);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
|
||||
{
|
||||
int i;
|
||||
|
||||
WARN_ON(idx > ale->params.ale_entries);
|
||||
|
||||
for (i = 0; i < ALE_ENTRY_WORDS; i++)
|
||||
__raw_writel(ale_entry[i], ale->params.ale_regs +
|
||||
ALE_TABLE + 4 * i);
|
||||
|
||||
__raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs +
|
||||
ALE_TABLE_CONTROL);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int type, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
u8 entry_addr[6];
|
||||
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
type = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
|
||||
continue;
|
||||
if (cpsw_ale_get_vlan_id(ale_entry) != vid)
|
||||
continue;
|
||||
cpsw_ale_get_addr(ale_entry, entry_addr);
|
||||
if (ether_addr_equal(entry_addr, addr))
|
||||
return idx;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int type, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
type = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (type != ALE_TYPE_VLAN)
|
||||
continue;
|
||||
if (cpsw_ale_get_vlan_id(ale_entry) == vid)
|
||||
return idx;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int cpsw_ale_match_free(struct cpsw_ale *ale)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int type, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
type = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (type == ALE_TYPE_FREE)
|
||||
return idx;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int type, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
type = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
|
||||
continue;
|
||||
if (cpsw_ale_get_mcast(ale_entry))
|
||||
continue;
|
||||
type = cpsw_ale_get_ucast_type(ale_entry);
|
||||
if (type != ALE_UCAST_PERSISTANT &&
|
||||
type != ALE_UCAST_OUI)
|
||||
return idx;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
|
||||
int port_mask)
|
||||
{
|
||||
int mask;
|
||||
|
||||
mask = cpsw_ale_get_port_mask(ale_entry);
|
||||
if ((mask & port_mask) == 0)
|
||||
return; /* ports dont intersect, not interested */
|
||||
mask &= ~port_mask;
|
||||
|
||||
/* free if only remaining port is host port */
|
||||
if (mask)
|
||||
cpsw_ale_set_port_mask(ale_entry, mask);
|
||||
else
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
|
||||
}
|
||||
|
||||
int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int ret, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
ret = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
|
||||
continue;
|
||||
|
||||
/* if vid passed is -1 then remove all multicast entry from
|
||||
* the table irrespective of vlan id, if a valid vlan id is
|
||||
* passed then remove only multicast added to that vlan id.
|
||||
* if vlan id doesn't match then move on to next entry.
|
||||
*/
|
||||
if (vid != -1 && cpsw_ale_get_vlan_id(ale_entry) != vid)
|
||||
continue;
|
||||
|
||||
if (cpsw_ale_get_mcast(ale_entry)) {
|
||||
u8 addr[6];
|
||||
|
||||
cpsw_ale_get_addr(ale_entry, addr);
|
||||
if (!is_broadcast_ether_addr(addr))
|
||||
cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
|
||||
}
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
|
||||
int port_mask)
|
||||
{
|
||||
int port;
|
||||
|
||||
port = cpsw_ale_get_port_num(ale_entry);
|
||||
if ((BIT(port) & port_mask) == 0)
|
||||
return; /* ports dont intersect, not interested */
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
|
||||
}
|
||||
|
||||
int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int ret, idx;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
ret = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
|
||||
continue;
|
||||
|
||||
if (cpsw_ale_get_mcast(ale_entry))
|
||||
cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
|
||||
else
|
||||
cpsw_ale_flush_ucast(ale, ale_entry, port_mask);
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry,
|
||||
int flags, u16 vid)
|
||||
{
|
||||
if (flags & ALE_VLAN) {
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR);
|
||||
cpsw_ale_set_vlan_id(ale_entry, vid);
|
||||
} else {
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
|
||||
}
|
||||
}
|
||||
|
||||
int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
|
||||
int flags, u16 vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx;
|
||||
|
||||
cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
|
||||
|
||||
cpsw_ale_set_addr(ale_entry, addr);
|
||||
cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
|
||||
cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
|
||||
cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
|
||||
cpsw_ale_set_port_num(ale_entry, port);
|
||||
|
||||
idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_match_free(ale);
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_find_ageable(ale);
|
||||
if (idx < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
|
||||
int flags, u16 vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx;
|
||||
|
||||
idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
|
||||
if (idx < 0)
|
||||
return -ENOENT;
|
||||
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
|
||||
int flags, u16 vid, int mcast_state)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx, mask;
|
||||
|
||||
idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
|
||||
if (idx >= 0)
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
|
||||
cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
|
||||
|
||||
cpsw_ale_set_addr(ale_entry, addr);
|
||||
cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
|
||||
cpsw_ale_set_mcast_state(ale_entry, mcast_state);
|
||||
|
||||
mask = cpsw_ale_get_port_mask(ale_entry);
|
||||
port_mask |= mask;
|
||||
cpsw_ale_set_port_mask(ale_entry, port_mask);
|
||||
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_match_free(ale);
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_find_ageable(ale);
|
||||
if (idx < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
|
||||
int flags, u16 vid)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx;
|
||||
|
||||
idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
|
||||
if (idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
|
||||
if (port_mask)
|
||||
cpsw_ale_set_port_mask(ale_entry, port_mask);
|
||||
else
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
|
||||
int reg_mcast, int unreg_mcast)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx;
|
||||
|
||||
idx = cpsw_ale_match_vlan(ale, vid);
|
||||
if (idx >= 0)
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
|
||||
cpsw_ale_set_vlan_id(ale_entry, vid);
|
||||
|
||||
cpsw_ale_set_vlan_untag_force(ale_entry, untag);
|
||||
cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
|
||||
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
|
||||
cpsw_ale_set_vlan_member_list(ale_entry, port);
|
||||
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_match_free(ale);
|
||||
if (idx < 0)
|
||||
idx = cpsw_ale_find_ageable(ale);
|
||||
if (idx < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
|
||||
int idx;
|
||||
|
||||
idx = cpsw_ale_match_vlan(ale, vid);
|
||||
if (idx < 0)
|
||||
return -ENOENT;
|
||||
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
|
||||
if (port_mask)
|
||||
cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
|
||||
else
|
||||
cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
|
||||
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
|
||||
{
|
||||
u32 ale_entry[ALE_ENTRY_WORDS];
|
||||
int type, idx;
|
||||
int unreg_mcast = 0;
|
||||
|
||||
/* Only bother doing the work if the setting is actually changing */
|
||||
if (ale->allmulti == allmulti)
|
||||
return;
|
||||
|
||||
/* Remember the new setting to check against next time */
|
||||
ale->allmulti = allmulti;
|
||||
|
||||
for (idx = 0; idx < ale->params.ale_entries; idx++) {
|
||||
cpsw_ale_read(ale, idx, ale_entry);
|
||||
type = cpsw_ale_get_entry_type(ale_entry);
|
||||
if (type != ALE_TYPE_VLAN)
|
||||
continue;
|
||||
|
||||
unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
|
||||
if (allmulti)
|
||||
unreg_mcast |= 1;
|
||||
else
|
||||
unreg_mcast &= ~1;
|
||||
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
|
||||
cpsw_ale_write(ale, idx, ale_entry);
|
||||
}
|
||||
}
|
||||
|
||||
struct ale_control_info {
|
||||
const char *name;
|
||||
int offset, port_offset;
|
||||
int shift, port_shift;
|
||||
int bits;
|
||||
};
|
||||
|
||||
static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
|
||||
[ALE_ENABLE] = {
|
||||
.name = "enable",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 31,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_CLEAR] = {
|
||||
.name = "clear",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 30,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_AGEOUT] = {
|
||||
.name = "ageout",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 29,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_P0_UNI_FLOOD] = {
|
||||
.name = "port0_unicast_flood",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 8,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_VLAN_NOLEARN] = {
|
||||
.name = "vlan_nolearn",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 7,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_NO_PORT_VLAN] = {
|
||||
.name = "no_port_vlan",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 6,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_OUI_DENY] = {
|
||||
.name = "oui_deny",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 5,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_BYPASS] = {
|
||||
.name = "bypass",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 4,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_RATE_LIMIT_TX] = {
|
||||
.name = "rate_limit_tx",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 3,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_VLAN_AWARE] = {
|
||||
.name = "vlan_aware",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 2,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_AUTH_ENABLE] = {
|
||||
.name = "auth_enable",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 1,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_RATE_LIMIT] = {
|
||||
.name = "rate_limit",
|
||||
.offset = ALE_CONTROL,
|
||||
.port_offset = 0,
|
||||
.shift = 0,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_PORT_STATE] = {
|
||||
.name = "port_state",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 0,
|
||||
.port_shift = 0,
|
||||
.bits = 2,
|
||||
},
|
||||
[ALE_PORT_DROP_UNTAGGED] = {
|
||||
.name = "drop_untagged",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 2,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_PORT_DROP_UNKNOWN_VLAN] = {
|
||||
.name = "drop_unknown",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 3,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_PORT_NOLEARN] = {
|
||||
.name = "nolearn",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 4,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_PORT_NO_SA_UPDATE] = {
|
||||
.name = "no_source_update",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 5,
|
||||
.port_shift = 0,
|
||||
.bits = 1,
|
||||
},
|
||||
[ALE_PORT_MCAST_LIMIT] = {
|
||||
.name = "mcast_limit",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 16,
|
||||
.port_shift = 0,
|
||||
.bits = 8,
|
||||
},
|
||||
[ALE_PORT_BCAST_LIMIT] = {
|
||||
.name = "bcast_limit",
|
||||
.offset = ALE_PORTCTL,
|
||||
.port_offset = 4,
|
||||
.shift = 24,
|
||||
.port_shift = 0,
|
||||
.bits = 8,
|
||||
},
|
||||
[ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
|
||||
.name = "unknown_vlan_member",
|
||||
.offset = ALE_UNKNOWNVLAN,
|
||||
.port_offset = 0,
|
||||
.shift = 0,
|
||||
.port_shift = 0,
|
||||
.bits = 6,
|
||||
},
|
||||
[ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
|
||||
.name = "unknown_mcast_flood",
|
||||
.offset = ALE_UNKNOWNVLAN,
|
||||
.port_offset = 0,
|
||||
.shift = 8,
|
||||
.port_shift = 0,
|
||||
.bits = 6,
|
||||
},
|
||||
[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
|
||||
.name = "unknown_reg_flood",
|
||||
.offset = ALE_UNKNOWNVLAN,
|
||||
.port_offset = 0,
|
||||
.shift = 16,
|
||||
.port_shift = 0,
|
||||
.bits = 6,
|
||||
},
|
||||
[ALE_PORT_UNTAGGED_EGRESS] = {
|
||||
.name = "untagged_egress",
|
||||
.offset = ALE_UNKNOWNVLAN,
|
||||
.port_offset = 0,
|
||||
.shift = 24,
|
||||
.port_shift = 0,
|
||||
.bits = 6,
|
||||
},
|
||||
};
|
||||
|
||||
int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
|
||||
int value)
|
||||
{
|
||||
const struct ale_control_info *info;
|
||||
int offset, shift;
|
||||
u32 tmp, mask;
|
||||
|
||||
if (control < 0 || control >= ARRAY_SIZE(ale_controls))
|
||||
return -EINVAL;
|
||||
|
||||
info = &ale_controls[control];
|
||||
if (info->port_offset == 0 && info->port_shift == 0)
|
||||
port = 0; /* global, port is a dont care */
|
||||
|
||||
if (port < 0 || port > ale->params.ale_ports)
|
||||
return -EINVAL;
|
||||
|
||||
mask = BITMASK(info->bits);
|
||||
if (value & ~mask)
|
||||
return -EINVAL;
|
||||
|
||||
offset = info->offset + (port * info->port_offset);
|
||||
shift = info->shift + (port * info->port_shift);
|
||||
|
||||
tmp = __raw_readl(ale->params.ale_regs + offset);
|
||||
tmp = (tmp & ~(mask << shift)) | (value << shift);
|
||||
__raw_writel(tmp, ale->params.ale_regs + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
|
||||
{
|
||||
const struct ale_control_info *info;
|
||||
int offset, shift;
|
||||
u32 tmp;
|
||||
|
||||
if (control < 0 || control >= ARRAY_SIZE(ale_controls))
|
||||
return -EINVAL;
|
||||
|
||||
info = &ale_controls[control];
|
||||
if (info->port_offset == 0 && info->port_shift == 0)
|
||||
port = 0; /* global, port is a dont care */
|
||||
|
||||
if (port < 0 || port > ale->params.ale_ports)
|
||||
return -EINVAL;
|
||||
|
||||
offset = info->offset + (port * info->port_offset);
|
||||
shift = info->shift + (port * info->port_shift);
|
||||
|
||||
tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
|
||||
return tmp & BITMASK(info->bits);
|
||||
}
|
||||
|
||||
static void cpsw_ale_timer(unsigned long arg)
|
||||
{
|
||||
struct cpsw_ale *ale = (struct cpsw_ale *)arg;
|
||||
|
||||
cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
|
||||
|
||||
if (ale->ageout) {
|
||||
ale->timer.expires = jiffies + ale->ageout;
|
||||
add_timer(&ale->timer);
|
||||
}
|
||||
}
|
||||
|
||||
int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
|
||||
{
|
||||
del_timer_sync(&ale->timer);
|
||||
ale->ageout = ageout * HZ;
|
||||
if (ale->ageout) {
|
||||
ale->timer.expires = jiffies + ale->ageout;
|
||||
add_timer(&ale->timer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpsw_ale_start(struct cpsw_ale *ale)
|
||||
{
|
||||
u32 rev;
|
||||
|
||||
rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
|
||||
dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
|
||||
ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
|
||||
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
|
||||
cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
|
||||
|
||||
init_timer(&ale->timer);
|
||||
ale->timer.data = (unsigned long)ale;
|
||||
ale->timer.function = cpsw_ale_timer;
|
||||
if (ale->ageout) {
|
||||
ale->timer.expires = jiffies + ale->ageout;
|
||||
add_timer(&ale->timer);
|
||||
}
|
||||
}
|
||||
|
||||
void cpsw_ale_stop(struct cpsw_ale *ale)
|
||||
{
|
||||
del_timer_sync(&ale->timer);
|
||||
}
|
||||
|
||||
struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
|
||||
{
|
||||
struct cpsw_ale *ale;
|
||||
|
||||
ale = kzalloc(sizeof(*ale), GFP_KERNEL);
|
||||
if (!ale)
|
||||
return NULL;
|
||||
|
||||
ale->params = *params;
|
||||
ale->ageout = ale->params.ale_ageout * HZ;
|
||||
|
||||
return ale;
|
||||
}
|
||||
|
||||
int cpsw_ale_destroy(struct cpsw_ale *ale)
|
||||
{
|
||||
if (!ale)
|
||||
return -EINVAL;
|
||||
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
|
||||
kfree(ale);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ale->params.ale_entries; i++) {
|
||||
cpsw_ale_read(ale, i, data);
|
||||
data += ALE_ENTRY_WORDS;
|
||||
}
|
||||
}
|
114
drivers/net/ethernet/ti/cpsw_ale.h
Normal file
114
drivers/net/ethernet/ti/cpsw_ale.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Texas Instruments 3-Port Ethernet Switch Address Lookup Engine APIs
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __TI_CPSW_ALE_H__
|
||||
#define __TI_CPSW_ALE_H__
|
||||
|
||||
struct cpsw_ale_params {
|
||||
struct device *dev;
|
||||
void __iomem *ale_regs;
|
||||
unsigned long ale_ageout; /* in secs */
|
||||
unsigned long ale_entries;
|
||||
unsigned long ale_ports;
|
||||
};
|
||||
|
||||
struct cpsw_ale {
|
||||
struct cpsw_ale_params params;
|
||||
struct timer_list timer;
|
||||
unsigned long ageout;
|
||||
int allmulti;
|
||||
};
|
||||
|
||||
enum cpsw_ale_control {
|
||||
/* global */
|
||||
ALE_ENABLE,
|
||||
ALE_CLEAR,
|
||||
ALE_AGEOUT,
|
||||
ALE_P0_UNI_FLOOD,
|
||||
ALE_VLAN_NOLEARN,
|
||||
ALE_NO_PORT_VLAN,
|
||||
ALE_OUI_DENY,
|
||||
ALE_BYPASS,
|
||||
ALE_RATE_LIMIT_TX,
|
||||
ALE_VLAN_AWARE,
|
||||
ALE_AUTH_ENABLE,
|
||||
ALE_RATE_LIMIT,
|
||||
/* port controls */
|
||||
ALE_PORT_STATE,
|
||||
ALE_PORT_DROP_UNTAGGED,
|
||||
ALE_PORT_DROP_UNKNOWN_VLAN,
|
||||
ALE_PORT_NOLEARN,
|
||||
ALE_PORT_NO_SA_UPDATE,
|
||||
ALE_PORT_UNKNOWN_VLAN_MEMBER,
|
||||
ALE_PORT_UNKNOWN_MCAST_FLOOD,
|
||||
ALE_PORT_UNKNOWN_REG_MCAST_FLOOD,
|
||||
ALE_PORT_UNTAGGED_EGRESS,
|
||||
ALE_PORT_BCAST_LIMIT,
|
||||
ALE_PORT_MCAST_LIMIT,
|
||||
ALE_NUM_CONTROLS,
|
||||
};
|
||||
|
||||
enum cpsw_ale_port_state {
|
||||
ALE_PORT_STATE_DISABLE = 0x00,
|
||||
ALE_PORT_STATE_BLOCK = 0x01,
|
||||
ALE_PORT_STATE_LEARN = 0x02,
|
||||
ALE_PORT_STATE_FORWARD = 0x03,
|
||||
};
|
||||
|
||||
/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */
|
||||
#define ALE_SECURE BIT(0)
|
||||
#define ALE_BLOCKED BIT(1)
|
||||
#define ALE_SUPER BIT(2)
|
||||
#define ALE_VLAN BIT(3)
|
||||
|
||||
#define ALE_PORT_HOST BIT(0)
|
||||
#define ALE_PORT_1 BIT(1)
|
||||
#define ALE_PORT_2 BIT(2)
|
||||
|
||||
#define ALE_MCAST_FWD 0
|
||||
#define ALE_MCAST_BLOCK_LEARN_FWD 1
|
||||
#define ALE_MCAST_FWD_LEARN 2
|
||||
#define ALE_MCAST_FWD_2 3
|
||||
|
||||
#define ALE_ENTRY_BITS 68
|
||||
#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
|
||||
|
||||
struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params);
|
||||
int cpsw_ale_destroy(struct cpsw_ale *ale);
|
||||
|
||||
void cpsw_ale_start(struct cpsw_ale *ale);
|
||||
void cpsw_ale_stop(struct cpsw_ale *ale);
|
||||
|
||||
int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout);
|
||||
int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask);
|
||||
int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid);
|
||||
int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
|
||||
int flags, u16 vid);
|
||||
int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
|
||||
int flags, u16 vid);
|
||||
int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
|
||||
int flags, u16 vid, int mcast_state);
|
||||
int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
|
||||
int flags, u16 vid);
|
||||
int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
|
||||
int reg_mcast, int unreg_mcast);
|
||||
int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
|
||||
void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti);
|
||||
|
||||
int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
|
||||
int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
|
||||
int control, int value);
|
||||
void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data);
|
||||
|
||||
#endif
|
414
drivers/net/ethernet/ti/cpts.c
Normal file
414
drivers/net/ethernet/ti/cpts.c
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* TI Common Platform Time Sync
|
||||
*
|
||||
* Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/ptp_classify.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include "cpts.h"
|
||||
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
|
||||
#define cpts_read32(c, r) __raw_readl(&c->reg->r)
|
||||
#define cpts_write32(c, v, r) __raw_writel(v, &c->reg->r)
|
||||
|
||||
static int event_expired(struct cpts_event *event)
|
||||
{
|
||||
return time_after(jiffies, event->tmo);
|
||||
}
|
||||
|
||||
static int event_type(struct cpts_event *event)
|
||||
{
|
||||
return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK;
|
||||
}
|
||||
|
||||
static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low)
|
||||
{
|
||||
u32 r = cpts_read32(cpts, intstat_raw);
|
||||
|
||||
if (r & TS_PEND_RAW) {
|
||||
*high = cpts_read32(cpts, event_high);
|
||||
*low = cpts_read32(cpts, event_low);
|
||||
cpts_write32(cpts, EVENT_POP, event_pop);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns zero if matching event type was found.
|
||||
*/
|
||||
static int cpts_fifo_read(struct cpts *cpts, int match)
|
||||
{
|
||||
int i, type = -1;
|
||||
u32 hi, lo;
|
||||
struct cpts_event *event;
|
||||
|
||||
for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
|
||||
if (cpts_fifo_pop(cpts, &hi, &lo))
|
||||
break;
|
||||
if (list_empty(&cpts->pool)) {
|
||||
pr_err("cpts: event pool is empty\n");
|
||||
return -1;
|
||||
}
|
||||
event = list_first_entry(&cpts->pool, struct cpts_event, list);
|
||||
event->tmo = jiffies + 2;
|
||||
event->high = hi;
|
||||
event->low = lo;
|
||||
type = event_type(event);
|
||||
switch (type) {
|
||||
case CPTS_EV_PUSH:
|
||||
case CPTS_EV_RX:
|
||||
case CPTS_EV_TX:
|
||||
list_del_init(&event->list);
|
||||
list_add_tail(&event->list, &cpts->events);
|
||||
break;
|
||||
case CPTS_EV_ROLL:
|
||||
case CPTS_EV_HALF:
|
||||
case CPTS_EV_HW:
|
||||
break;
|
||||
default:
|
||||
pr_err("cpts: unknown event type\n");
|
||||
break;
|
||||
}
|
||||
if (type == match)
|
||||
break;
|
||||
}
|
||||
return type == match ? 0 : -1;
|
||||
}
|
||||
|
||||
static cycle_t cpts_systim_read(const struct cyclecounter *cc)
|
||||
{
|
||||
u64 val = 0;
|
||||
struct cpts_event *event;
|
||||
struct list_head *this, *next;
|
||||
struct cpts *cpts = container_of(cc, struct cpts, cc);
|
||||
|
||||
cpts_write32(cpts, TS_PUSH, ts_push);
|
||||
if (cpts_fifo_read(cpts, CPTS_EV_PUSH))
|
||||
pr_err("cpts: unable to obtain a time stamp\n");
|
||||
|
||||
list_for_each_safe(this, next, &cpts->events) {
|
||||
event = list_entry(this, struct cpts_event, list);
|
||||
if (event_type(event) == CPTS_EV_PUSH) {
|
||||
list_del_init(&event->list);
|
||||
list_add(&event->list, &cpts->pool);
|
||||
val = event->low;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* PTP clock operations */
|
||||
|
||||
static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
|
||||
{
|
||||
u64 adj;
|
||||
u32 diff, mult;
|
||||
int neg_adj = 0;
|
||||
unsigned long flags;
|
||||
struct cpts *cpts = container_of(ptp, struct cpts, info);
|
||||
|
||||
if (ppb < 0) {
|
||||
neg_adj = 1;
|
||||
ppb = -ppb;
|
||||
}
|
||||
mult = cpts->cc_mult;
|
||||
adj = mult;
|
||||
adj *= ppb;
|
||||
diff = div_u64(adj, 1000000000ULL);
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
|
||||
timecounter_read(&cpts->tc);
|
||||
|
||||
cpts->cc.mult = neg_adj ? mult - diff : mult + diff;
|
||||
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
s64 now;
|
||||
unsigned long flags;
|
||||
struct cpts *cpts = container_of(ptp, struct cpts, info);
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
now = timecounter_read(&cpts->tc);
|
||||
now += delta;
|
||||
timecounter_init(&cpts->tc, &cpts->cc, now);
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
|
||||
{
|
||||
u64 ns;
|
||||
u32 remainder;
|
||||
unsigned long flags;
|
||||
struct cpts *cpts = container_of(ptp, struct cpts, info);
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
ns = timecounter_read(&cpts->tc);
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
|
||||
ts->tv_nsec = remainder;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpts_ptp_settime(struct ptp_clock_info *ptp,
|
||||
const struct timespec *ts)
|
||||
{
|
||||
u64 ns;
|
||||
unsigned long flags;
|
||||
struct cpts *cpts = container_of(ptp, struct cpts, info);
|
||||
|
||||
ns = ts->tv_sec * 1000000000ULL;
|
||||
ns += ts->tv_nsec;
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
timecounter_init(&cpts->tc, &cpts->cc, ns);
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpts_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static struct ptp_clock_info cpts_info = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "CTPS timer",
|
||||
.max_adj = 1000000,
|
||||
.n_ext_ts = 0,
|
||||
.n_pins = 0,
|
||||
.pps = 0,
|
||||
.adjfreq = cpts_ptp_adjfreq,
|
||||
.adjtime = cpts_ptp_adjtime,
|
||||
.gettime = cpts_ptp_gettime,
|
||||
.settime = cpts_ptp_settime,
|
||||
.enable = cpts_ptp_enable,
|
||||
};
|
||||
|
||||
static void cpts_overflow_check(struct work_struct *work)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct cpts *cpts = container_of(work, struct cpts, overflow_work.work);
|
||||
|
||||
cpts_write32(cpts, CPTS_EN, control);
|
||||
cpts_write32(cpts, TS_PEND_EN, int_enable);
|
||||
cpts_ptp_gettime(&cpts->info, &ts);
|
||||
pr_debug("cpts overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec);
|
||||
schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
|
||||
}
|
||||
|
||||
static void cpts_clk_init(struct device *dev, struct cpts *cpts)
|
||||
{
|
||||
cpts->refclk = devm_clk_get(dev, "cpts");
|
||||
if (IS_ERR(cpts->refclk)) {
|
||||
dev_err(dev, "Failed to get cpts refclk\n");
|
||||
cpts->refclk = NULL;
|
||||
return;
|
||||
}
|
||||
clk_prepare_enable(cpts->refclk);
|
||||
}
|
||||
|
||||
static void cpts_clk_release(struct cpts *cpts)
|
||||
{
|
||||
clk_disable(cpts->refclk);
|
||||
}
|
||||
|
||||
static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
|
||||
u16 ts_seqid, u8 ts_msgtype)
|
||||
{
|
||||
u16 *seqid;
|
||||
unsigned int offset = 0;
|
||||
u8 *msgtype, *data = skb->data;
|
||||
|
||||
if (ptp_class & PTP_CLASS_VLAN)
|
||||
offset += VLAN_HLEN;
|
||||
|
||||
switch (ptp_class & PTP_CLASS_PMASK) {
|
||||
case PTP_CLASS_IPV4:
|
||||
offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
|
||||
break;
|
||||
case PTP_CLASS_IPV6:
|
||||
offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
|
||||
break;
|
||||
case PTP_CLASS_L2:
|
||||
offset += ETH_HLEN;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid))
|
||||
return 0;
|
||||
|
||||
if (unlikely(ptp_class & PTP_CLASS_V1))
|
||||
msgtype = data + offset + OFF_PTP_CONTROL;
|
||||
else
|
||||
msgtype = data + offset;
|
||||
|
||||
seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
|
||||
|
||||
return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid));
|
||||
}
|
||||
|
||||
static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
|
||||
{
|
||||
u64 ns = 0;
|
||||
struct cpts_event *event;
|
||||
struct list_head *this, *next;
|
||||
unsigned int class = ptp_classify_raw(skb);
|
||||
unsigned long flags;
|
||||
u16 seqid;
|
||||
u8 mtype;
|
||||
|
||||
if (class == PTP_CLASS_NONE)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
cpts_fifo_read(cpts, CPTS_EV_PUSH);
|
||||
list_for_each_safe(this, next, &cpts->events) {
|
||||
event = list_entry(this, struct cpts_event, list);
|
||||
if (event_expired(event)) {
|
||||
list_del_init(&event->list);
|
||||
list_add(&event->list, &cpts->pool);
|
||||
continue;
|
||||
}
|
||||
mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
|
||||
seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK;
|
||||
if (ev_type == event_type(event) &&
|
||||
cpts_match(skb, class, seqid, mtype)) {
|
||||
ns = timecounter_cyc2time(&cpts->tc, event->low);
|
||||
list_del_init(&event->list);
|
||||
list_add(&event->list, &cpts->pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
|
||||
{
|
||||
u64 ns;
|
||||
struct skb_shared_hwtstamps *ssh;
|
||||
|
||||
if (!cpts->rx_enable)
|
||||
return;
|
||||
ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
|
||||
if (!ns)
|
||||
return;
|
||||
ssh = skb_hwtstamps(skb);
|
||||
memset(ssh, 0, sizeof(*ssh));
|
||||
ssh->hwtstamp = ns_to_ktime(ns);
|
||||
}
|
||||
|
||||
void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
|
||||
{
|
||||
u64 ns;
|
||||
struct skb_shared_hwtstamps ssh;
|
||||
|
||||
if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
|
||||
return;
|
||||
ns = cpts_find_ts(cpts, skb, CPTS_EV_TX);
|
||||
if (!ns)
|
||||
return;
|
||||
memset(&ssh, 0, sizeof(ssh));
|
||||
ssh.hwtstamp = ns_to_ktime(ns);
|
||||
skb_tstamp_tx(skb, &ssh);
|
||||
}
|
||||
|
||||
#endif /*CONFIG_TI_CPTS*/
|
||||
|
||||
int cpts_register(struct device *dev, struct cpts *cpts,
|
||||
u32 mult, u32 shift)
|
||||
{
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
int err, i;
|
||||
unsigned long flags;
|
||||
|
||||
cpts->info = cpts_info;
|
||||
cpts->clock = ptp_clock_register(&cpts->info, dev);
|
||||
if (IS_ERR(cpts->clock)) {
|
||||
err = PTR_ERR(cpts->clock);
|
||||
cpts->clock = NULL;
|
||||
return err;
|
||||
}
|
||||
spin_lock_init(&cpts->lock);
|
||||
|
||||
cpts->cc.read = cpts_systim_read;
|
||||
cpts->cc.mask = CLOCKSOURCE_MASK(32);
|
||||
cpts->cc_mult = mult;
|
||||
cpts->cc.mult = mult;
|
||||
cpts->cc.shift = shift;
|
||||
|
||||
INIT_LIST_HEAD(&cpts->events);
|
||||
INIT_LIST_HEAD(&cpts->pool);
|
||||
for (i = 0; i < CPTS_MAX_EVENTS; i++)
|
||||
list_add(&cpts->pool_data[i].list, &cpts->pool);
|
||||
|
||||
cpts_clk_init(dev, cpts);
|
||||
cpts_write32(cpts, CPTS_EN, control);
|
||||
cpts_write32(cpts, TS_PEND_EN, int_enable);
|
||||
|
||||
spin_lock_irqsave(&cpts->lock, flags);
|
||||
timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real()));
|
||||
spin_unlock_irqrestore(&cpts->lock, flags);
|
||||
|
||||
INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check);
|
||||
schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
|
||||
|
||||
cpts->phc_index = ptp_clock_index(cpts->clock);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cpts_unregister(struct cpts *cpts)
|
||||
{
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
if (cpts->clock) {
|
||||
ptp_clock_unregister(cpts->clock);
|
||||
cancel_delayed_work_sync(&cpts->overflow_work);
|
||||
}
|
||||
if (cpts->refclk)
|
||||
cpts_clk_release(cpts);
|
||||
#endif
|
||||
}
|
144
drivers/net/ethernet/ti/cpts.h
Normal file
144
drivers/net/ethernet/ti/cpts.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* TI Common Platform Time Sync
|
||||
*
|
||||
* Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef _TI_CPTS_H_
|
||||
#define _TI_CPTS_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
struct cpsw_cpts {
|
||||
u32 idver; /* Identification and version */
|
||||
u32 control; /* Time sync control */
|
||||
u32 res1;
|
||||
u32 ts_push; /* Time stamp event push */
|
||||
u32 ts_load_val; /* Time stamp load value */
|
||||
u32 ts_load_en; /* Time stamp load enable */
|
||||
u32 res2[2];
|
||||
u32 intstat_raw; /* Time sync interrupt status raw */
|
||||
u32 intstat_masked; /* Time sync interrupt status masked */
|
||||
u32 int_enable; /* Time sync interrupt enable */
|
||||
u32 res3;
|
||||
u32 event_pop; /* Event interrupt pop */
|
||||
u32 event_low; /* 32 Bit Event Time Stamp */
|
||||
u32 event_high; /* Event Type Fields */
|
||||
};
|
||||
|
||||
/* Bit definitions for the IDVER register */
|
||||
#define TX_IDENT_SHIFT (16) /* TX Identification Value */
|
||||
#define TX_IDENT_MASK (0xffff)
|
||||
#define RTL_VER_SHIFT (11) /* RTL Version Value */
|
||||
#define RTL_VER_MASK (0x1f)
|
||||
#define MAJOR_VER_SHIFT (8) /* Major Version Value */
|
||||
#define MAJOR_VER_MASK (0x7)
|
||||
#define MINOR_VER_SHIFT (0) /* Minor Version Value */
|
||||
#define MINOR_VER_MASK (0xff)
|
||||
|
||||
/* Bit definitions for the CONTROL register */
|
||||
#define HW4_TS_PUSH_EN (1<<11) /* Hardware push 4 enable */
|
||||
#define HW3_TS_PUSH_EN (1<<10) /* Hardware push 3 enable */
|
||||
#define HW2_TS_PUSH_EN (1<<9) /* Hardware push 2 enable */
|
||||
#define HW1_TS_PUSH_EN (1<<8) /* Hardware push 1 enable */
|
||||
#define INT_TEST (1<<1) /* Interrupt Test */
|
||||
#define CPTS_EN (1<<0) /* Time Sync Enable */
|
||||
|
||||
/*
|
||||
* Definitions for the single bit resisters:
|
||||
* TS_PUSH TS_LOAD_EN INTSTAT_RAW INTSTAT_MASKED INT_ENABLE EVENT_POP
|
||||
*/
|
||||
#define TS_PUSH (1<<0) /* Time stamp event push */
|
||||
#define TS_LOAD_EN (1<<0) /* Time Stamp Load */
|
||||
#define TS_PEND_RAW (1<<0) /* int read (before enable) */
|
||||
#define TS_PEND (1<<0) /* masked interrupt read (after enable) */
|
||||
#define TS_PEND_EN (1<<0) /* masked interrupt enable */
|
||||
#define EVENT_POP (1<<0) /* writing discards one event */
|
||||
|
||||
/* Bit definitions for the EVENT_HIGH register */
|
||||
#define PORT_NUMBER_SHIFT (24) /* Indicates Ethernet port or HW pin */
|
||||
#define PORT_NUMBER_MASK (0x1f)
|
||||
#define EVENT_TYPE_SHIFT (20) /* Time sync event type */
|
||||
#define EVENT_TYPE_MASK (0xf)
|
||||
#define MESSAGE_TYPE_SHIFT (16) /* PTP message type */
|
||||
#define MESSAGE_TYPE_MASK (0xf)
|
||||
#define SEQUENCE_ID_SHIFT (0) /* PTP message sequence ID */
|
||||
#define SEQUENCE_ID_MASK (0xffff)
|
||||
|
||||
enum {
|
||||
CPTS_EV_PUSH, /* Time Stamp Push Event */
|
||||
CPTS_EV_ROLL, /* Time Stamp Rollover Event */
|
||||
CPTS_EV_HALF, /* Time Stamp Half Rollover Event */
|
||||
CPTS_EV_HW, /* Hardware Time Stamp Push Event */
|
||||
CPTS_EV_RX, /* Ethernet Receive Event */
|
||||
CPTS_EV_TX, /* Ethernet Transmit Event */
|
||||
};
|
||||
|
||||
/* This covers any input clock up to about 500 MHz. */
|
||||
#define CPTS_OVERFLOW_PERIOD (HZ * 8)
|
||||
|
||||
#define CPTS_FIFO_DEPTH 16
|
||||
#define CPTS_MAX_EVENTS 32
|
||||
|
||||
struct cpts_event {
|
||||
struct list_head list;
|
||||
unsigned long tmo;
|
||||
u32 high;
|
||||
u32 low;
|
||||
};
|
||||
|
||||
struct cpts {
|
||||
struct cpsw_cpts __iomem *reg;
|
||||
int tx_enable;
|
||||
int rx_enable;
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
struct ptp_clock_info info;
|
||||
struct ptp_clock *clock;
|
||||
spinlock_t lock; /* protects time registers */
|
||||
u32 cc_mult; /* for the nominal frequency */
|
||||
struct cyclecounter cc;
|
||||
struct timecounter tc;
|
||||
struct delayed_work overflow_work;
|
||||
int phc_index;
|
||||
struct clk *refclk;
|
||||
struct list_head events;
|
||||
struct list_head pool;
|
||||
struct cpts_event pool_data[CPTS_MAX_EVENTS];
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
|
||||
void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
|
||||
#else
|
||||
static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift);
|
||||
void cpts_unregister(struct cpts *cpts);
|
||||
|
||||
#endif
|
1038
drivers/net/ethernet/ti/davinci_cpdma.c
Normal file
1038
drivers/net/ethernet/ti/davinci_cpdma.c
Normal file
File diff suppressed because it is too large
Load diff
117
drivers/net/ethernet/ti/davinci_cpdma.h
Normal file
117
drivers/net/ethernet/ti/davinci_cpdma.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Texas Instruments CPDMA Driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __DAVINCI_CPDMA_H__
|
||||
#define __DAVINCI_CPDMA_H__
|
||||
|
||||
#define CPDMA_MAX_CHANNELS BITS_PER_LONG
|
||||
|
||||
#define tx_chan_num(chan) (chan)
|
||||
#define rx_chan_num(chan) ((chan) + CPDMA_MAX_CHANNELS)
|
||||
#define is_rx_chan(chan) ((chan)->chan_num >= CPDMA_MAX_CHANNELS)
|
||||
#define is_tx_chan(chan) (!is_rx_chan(chan))
|
||||
#define __chan_linear(chan_num) ((chan_num) & (CPDMA_MAX_CHANNELS - 1))
|
||||
#define chan_linear(chan) __chan_linear((chan)->chan_num)
|
||||
|
||||
#define CPDMA_RX_SOURCE_PORT(__status__) ((__status__ >> 16) & 0x7)
|
||||
|
||||
#define CPDMA_EOI_RX_THRESH 0x0
|
||||
#define CPDMA_EOI_RX 0x1
|
||||
#define CPDMA_EOI_TX 0x2
|
||||
#define CPDMA_EOI_MISC 0x3
|
||||
|
||||
struct cpdma_params {
|
||||
struct device *dev;
|
||||
void __iomem *dmaregs;
|
||||
void __iomem *txhdp, *rxhdp, *txcp, *rxcp;
|
||||
void __iomem *rxthresh, *rxfree;
|
||||
int num_chan;
|
||||
bool has_soft_reset;
|
||||
int min_packet_size;
|
||||
u32 desc_mem_phys;
|
||||
u32 desc_hw_addr;
|
||||
int desc_mem_size;
|
||||
int desc_align;
|
||||
|
||||
/*
|
||||
* Some instances of embedded cpdma controllers have extra control and
|
||||
* status registers. The following flag enables access to these
|
||||
* "extended" registers.
|
||||
*/
|
||||
bool has_ext_regs;
|
||||
};
|
||||
|
||||
struct cpdma_chan_stats {
|
||||
u32 head_enqueue;
|
||||
u32 tail_enqueue;
|
||||
u32 pad_enqueue;
|
||||
u32 misqueued;
|
||||
u32 desc_alloc_fail;
|
||||
u32 pad_alloc_fail;
|
||||
u32 runt_receive_buff;
|
||||
u32 runt_transmit_buff;
|
||||
u32 empty_dequeue;
|
||||
u32 busy_dequeue;
|
||||
u32 good_dequeue;
|
||||
u32 requeue;
|
||||
u32 teardown_dequeue;
|
||||
};
|
||||
|
||||
struct cpdma_ctlr;
|
||||
struct cpdma_chan;
|
||||
|
||||
typedef void (*cpdma_handler_fn)(void *token, int len, int status);
|
||||
|
||||
struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params);
|
||||
int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr);
|
||||
int cpdma_ctlr_start(struct cpdma_ctlr *ctlr);
|
||||
int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr);
|
||||
int cpdma_ctlr_dump(struct cpdma_ctlr *ctlr);
|
||||
|
||||
struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
|
||||
cpdma_handler_fn handler);
|
||||
int cpdma_chan_destroy(struct cpdma_chan *chan);
|
||||
int cpdma_chan_start(struct cpdma_chan *chan);
|
||||
int cpdma_chan_stop(struct cpdma_chan *chan);
|
||||
int cpdma_chan_dump(struct cpdma_chan *chan);
|
||||
|
||||
int cpdma_chan_get_stats(struct cpdma_chan *chan,
|
||||
struct cpdma_chan_stats *stats);
|
||||
int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data,
|
||||
int len, int directed);
|
||||
int cpdma_chan_process(struct cpdma_chan *chan, int quota);
|
||||
|
||||
int cpdma_ctlr_int_ctrl(struct cpdma_ctlr *ctlr, bool enable);
|
||||
void cpdma_ctlr_eoi(struct cpdma_ctlr *ctlr, u32 value);
|
||||
int cpdma_chan_int_ctrl(struct cpdma_chan *chan, bool enable);
|
||||
bool cpdma_check_free_tx_desc(struct cpdma_chan *chan);
|
||||
|
||||
enum cpdma_control {
|
||||
CPDMA_CMD_IDLE, /* write-only */
|
||||
CPDMA_COPY_ERROR_FRAMES, /* read-write */
|
||||
CPDMA_RX_OFF_LEN_UPDATE, /* read-write */
|
||||
CPDMA_RX_OWNERSHIP_FLIP, /* read-write */
|
||||
CPDMA_TX_PRIO_FIXED, /* read-write */
|
||||
CPDMA_STAT_IDLE, /* read-only */
|
||||
CPDMA_STAT_TX_ERR_CHAN, /* read-only */
|
||||
CPDMA_STAT_TX_ERR_CODE, /* read-only */
|
||||
CPDMA_STAT_RX_ERR_CHAN, /* read-only */
|
||||
CPDMA_STAT_RX_ERR_CODE, /* read-only */
|
||||
CPDMA_RX_BUFFER_OFFSET, /* read-write */
|
||||
};
|
||||
|
||||
int cpdma_control_get(struct cpdma_ctlr *ctlr, int control);
|
||||
int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value);
|
||||
|
||||
#endif
|
2120
drivers/net/ethernet/ti/davinci_emac.c
Normal file
2120
drivers/net/ethernet/ti/davinci_emac.c
Normal file
File diff suppressed because it is too large
Load diff
504
drivers/net/ethernet/ti/davinci_mdio.c
Normal file
504
drivers/net/ethernet/ti/davinci_mdio.c
Normal file
|
@ -0,0 +1,504 @@
|
|||
/*
|
||||
* DaVinci MDIO Module driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments.
|
||||
*
|
||||
* Shamelessly ripped out of davinci_emac.c, original copyrights follow:
|
||||
*
|
||||
* Copyright (C) 2009 Texas Instruments.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/davinci_emac.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/*
|
||||
* This timeout definition is a worst-case ultra defensive measure against
|
||||
* unexpected controller lock ups. Ideally, we should never ever hit this
|
||||
* scenario in practice.
|
||||
*/
|
||||
#define MDIO_TIMEOUT 100 /* msecs */
|
||||
|
||||
#define PHY_REG_MASK 0x1f
|
||||
#define PHY_ID_MASK 0x1f
|
||||
|
||||
#define DEF_OUT_FREQ 2200000 /* 2.2 MHz */
|
||||
|
||||
struct davinci_mdio_regs {
|
||||
u32 version;
|
||||
u32 control;
|
||||
#define CONTROL_IDLE BIT(31)
|
||||
#define CONTROL_ENABLE BIT(30)
|
||||
#define CONTROL_MAX_DIV (0xffff)
|
||||
|
||||
u32 alive;
|
||||
u32 link;
|
||||
u32 linkintraw;
|
||||
u32 linkintmasked;
|
||||
u32 __reserved_0[2];
|
||||
u32 userintraw;
|
||||
u32 userintmasked;
|
||||
u32 userintmaskset;
|
||||
u32 userintmaskclr;
|
||||
u32 __reserved_1[20];
|
||||
|
||||
struct {
|
||||
u32 access;
|
||||
#define USERACCESS_GO BIT(31)
|
||||
#define USERACCESS_WRITE BIT(30)
|
||||
#define USERACCESS_ACK BIT(29)
|
||||
#define USERACCESS_READ (0)
|
||||
#define USERACCESS_DATA (0xffff)
|
||||
|
||||
u32 physel;
|
||||
} user[0];
|
||||
};
|
||||
|
||||
static const struct mdio_platform_data default_pdata = {
|
||||
.bus_freq = DEF_OUT_FREQ,
|
||||
};
|
||||
|
||||
struct davinci_mdio_data {
|
||||
struct mdio_platform_data pdata;
|
||||
struct davinci_mdio_regs __iomem *regs;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
struct mii_bus *bus;
|
||||
bool suspended;
|
||||
unsigned long access_time; /* jiffies */
|
||||
/* Indicates that driver shouldn't modify phy_mask in case
|
||||
* if MDIO bus is registered from DT.
|
||||
*/
|
||||
bool skip_scan;
|
||||
};
|
||||
|
||||
static void __davinci_mdio_reset(struct davinci_mdio_data *data)
|
||||
{
|
||||
u32 mdio_in, div, mdio_out_khz, access_time;
|
||||
|
||||
mdio_in = clk_get_rate(data->clk);
|
||||
div = (mdio_in / data->pdata.bus_freq) - 1;
|
||||
if (div > CONTROL_MAX_DIV)
|
||||
div = CONTROL_MAX_DIV;
|
||||
|
||||
/* set enable and clock divider */
|
||||
__raw_writel(div | CONTROL_ENABLE, &data->regs->control);
|
||||
|
||||
/*
|
||||
* One mdio transaction consists of:
|
||||
* 32 bits of preamble
|
||||
* 32 bits of transferred data
|
||||
* 24 bits of bus yield (not needed unless shared?)
|
||||
*/
|
||||
mdio_out_khz = mdio_in / (1000 * (div + 1));
|
||||
access_time = (88 * 1000) / mdio_out_khz;
|
||||
|
||||
/*
|
||||
* In the worst case, we could be kicking off a user-access immediately
|
||||
* after the mdio bus scan state-machine triggered its own read. If
|
||||
* so, our request could get deferred by one access cycle. We
|
||||
* defensively allow for 4 access cycles.
|
||||
*/
|
||||
data->access_time = usecs_to_jiffies(access_time * 4);
|
||||
if (!data->access_time)
|
||||
data->access_time = 1;
|
||||
}
|
||||
|
||||
static int davinci_mdio_reset(struct mii_bus *bus)
|
||||
{
|
||||
struct davinci_mdio_data *data = bus->priv;
|
||||
u32 phy_mask, ver;
|
||||
|
||||
__davinci_mdio_reset(data);
|
||||
|
||||
/* wait for scan logic to settle */
|
||||
msleep(PHY_MAX_ADDR * data->access_time);
|
||||
|
||||
/* dump hardware version info */
|
||||
ver = __raw_readl(&data->regs->version);
|
||||
dev_info(data->dev, "davinci mdio revision %d.%d\n",
|
||||
(ver >> 8) & 0xff, ver & 0xff);
|
||||
|
||||
if (data->skip_scan)
|
||||
return 0;
|
||||
|
||||
/* get phy mask from the alive register */
|
||||
phy_mask = __raw_readl(&data->regs->alive);
|
||||
if (phy_mask) {
|
||||
/* restrict mdio bus to live phys only */
|
||||
dev_info(data->dev, "detected phy mask %x\n", ~phy_mask);
|
||||
phy_mask = ~phy_mask;
|
||||
} else {
|
||||
/* desperately scan all phys */
|
||||
dev_warn(data->dev, "no live phy, scanning all\n");
|
||||
phy_mask = 0;
|
||||
}
|
||||
data->bus->phy_mask = phy_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait until hardware is ready for another user access */
|
||||
static inline int wait_for_user_access(struct davinci_mdio_data *data)
|
||||
{
|
||||
struct davinci_mdio_regs __iomem *regs = data->regs;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT);
|
||||
u32 reg;
|
||||
|
||||
while (time_after(timeout, jiffies)) {
|
||||
reg = __raw_readl(®s->user[0].access);
|
||||
if ((reg & USERACCESS_GO) == 0)
|
||||
return 0;
|
||||
|
||||
reg = __raw_readl(®s->control);
|
||||
if ((reg & CONTROL_IDLE) == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* An emac soft_reset may have clobbered the mdio controller's
|
||||
* state machine. We need to reset and retry the current
|
||||
* operation
|
||||
*/
|
||||
dev_warn(data->dev, "resetting idled controller\n");
|
||||
__davinci_mdio_reset(data);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
reg = __raw_readl(®s->user[0].access);
|
||||
if ((reg & USERACCESS_GO) == 0)
|
||||
return 0;
|
||||
|
||||
dev_err(data->dev, "timed out waiting for user access\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* wait until hardware state machine is idle */
|
||||
static inline int wait_for_idle(struct davinci_mdio_data *data)
|
||||
{
|
||||
struct davinci_mdio_regs __iomem *regs = data->regs;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT);
|
||||
|
||||
while (time_after(timeout, jiffies)) {
|
||||
if (__raw_readl(®s->control) & CONTROL_IDLE)
|
||||
return 0;
|
||||
}
|
||||
dev_err(data->dev, "timed out waiting for idle\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
|
||||
{
|
||||
struct davinci_mdio_data *data = bus->priv;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&data->lock);
|
||||
|
||||
if (data->suspended) {
|
||||
spin_unlock(&data->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
|
||||
(phy_id << 16));
|
||||
|
||||
while (1) {
|
||||
ret = wait_for_user_access(data);
|
||||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
__raw_writel(reg, &data->regs->user[0].access);
|
||||
|
||||
ret = wait_for_user_access(data);
|
||||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
reg = __raw_readl(&data->regs->user[0].access);
|
||||
ret = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
|
||||
int phy_reg, u16 phy_data)
|
||||
{
|
||||
struct davinci_mdio_data *data = bus->priv;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&data->lock);
|
||||
|
||||
if (data->suspended) {
|
||||
spin_unlock(&data->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) |
|
||||
(phy_id << 16) | (phy_data & USERACCESS_DATA));
|
||||
|
||||
while (1) {
|
||||
ret = wait_for_user_access(data);
|
||||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
__raw_writel(reg, &data->regs->user[0].access);
|
||||
|
||||
ret = wait_for_user_access(data);
|
||||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
u32 prop;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(node, "bus_freq", &prop)) {
|
||||
dev_err(&pdev->dev, "Missing bus_freq property in the DT.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data->bus_freq = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int davinci_mdio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mdio_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct davinci_mdio_data *data;
|
||||
struct resource *res;
|
||||
struct phy_device *phy;
|
||||
int ret, addr;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->bus = devm_mdiobus_alloc(dev);
|
||||
if (!data->bus) {
|
||||
dev_err(dev, "failed to alloc mii bus\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (dev->of_node) {
|
||||
if (davinci_mdio_probe_dt(&data->pdata, pdev))
|
||||
data->pdata = default_pdata;
|
||||
snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
|
||||
} else {
|
||||
data->pdata = pdata ? (*pdata) : default_pdata;
|
||||
snprintf(data->bus->id, MII_BUS_ID_SIZE, "%s-%x",
|
||||
pdev->name, pdev->id);
|
||||
}
|
||||
|
||||
data->bus->name = dev_name(dev);
|
||||
data->bus->read = davinci_mdio_read,
|
||||
data->bus->write = davinci_mdio_write,
|
||||
data->bus->reset = davinci_mdio_reset,
|
||||
data->bus->parent = dev;
|
||||
data->bus->priv = data;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
data->clk = devm_clk_get(dev, "fck");
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(dev, "failed to get device clock\n");
|
||||
ret = PTR_ERR(data->clk);
|
||||
data->clk = NULL;
|
||||
goto bail_out;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
data->dev = dev;
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(data->regs)) {
|
||||
ret = PTR_ERR(data->regs);
|
||||
goto bail_out;
|
||||
}
|
||||
|
||||
/* register the mii bus
|
||||
* Create PHYs from DT only in case if PHY child nodes are explicitly
|
||||
* defined to support backward compatibility with DTs which assume that
|
||||
* Davinci MDIO will always scan the bus for PHYs detection.
|
||||
*/
|
||||
if (dev->of_node && of_get_child_count(dev->of_node)) {
|
||||
data->skip_scan = true;
|
||||
ret = of_mdiobus_register(data->bus, dev->of_node);
|
||||
} else {
|
||||
ret = mdiobus_register(data->bus);
|
||||
}
|
||||
if (ret)
|
||||
goto bail_out;
|
||||
|
||||
/* scan and dump the bus */
|
||||
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
|
||||
phy = data->bus->phy_map[addr];
|
||||
if (phy) {
|
||||
dev_info(dev, "phy[%d]: device %s, driver %s\n",
|
||||
phy->addr, dev_name(&phy->dev),
|
||||
phy->drv ? phy->drv->name : "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bail_out:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_mdio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_mdio_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
if (data->bus)
|
||||
mdiobus_unregister(data->bus);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_mdio_suspend(struct device *dev)
|
||||
{
|
||||
struct davinci_mdio_data *data = dev_get_drvdata(dev);
|
||||
u32 ctrl;
|
||||
|
||||
spin_lock(&data->lock);
|
||||
|
||||
/* shutdown the scan state machine */
|
||||
ctrl = __raw_readl(&data->regs->control);
|
||||
ctrl &= ~CONTROL_ENABLE;
|
||||
__raw_writel(ctrl, &data->regs->control);
|
||||
wait_for_idle(data);
|
||||
|
||||
data->suspended = true;
|
||||
spin_unlock(&data->lock);
|
||||
pm_runtime_put_sync(data->dev);
|
||||
|
||||
/* Select sleep pin state */
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_mdio_resume(struct device *dev)
|
||||
{
|
||||
struct davinci_mdio_data *data = dev_get_drvdata(dev);
|
||||
|
||||
/* Select default pin state */
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
pm_runtime_get_sync(data->dev);
|
||||
|
||||
spin_lock(&data->lock);
|
||||
/* restart the scan state machine */
|
||||
__davinci_mdio_reset(data);
|
||||
|
||||
data->suspended = false;
|
||||
spin_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops davinci_mdio_pm_ops = {
|
||||
.suspend_late = davinci_mdio_suspend,
|
||||
.resume_early = davinci_mdio_resume,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static const struct of_device_id davinci_mdio_of_mtable[] = {
|
||||
{ .compatible = "ti,davinci_mdio", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable);
|
||||
#endif
|
||||
|
||||
static struct platform_driver davinci_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "davinci_mdio",
|
||||
.pm = &davinci_mdio_pm_ops,
|
||||
.of_match_table = of_match_ptr(davinci_mdio_of_mtable),
|
||||
},
|
||||
.probe = davinci_mdio_probe,
|
||||
.remove = davinci_mdio_remove,
|
||||
};
|
||||
|
||||
static int __init davinci_mdio_init(void)
|
||||
{
|
||||
return platform_driver_register(&davinci_mdio_driver);
|
||||
}
|
||||
device_initcall(davinci_mdio_init);
|
||||
|
||||
static void __exit davinci_mdio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&davinci_mdio_driver);
|
||||
}
|
||||
module_exit(davinci_mdio_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("DaVinci MDIO driver");
|
3293
drivers/net/ethernet/ti/tlan.c
Normal file
3293
drivers/net/ethernet/ti/tlan.c
Normal file
File diff suppressed because it is too large
Load diff
544
drivers/net/ethernet/ti/tlan.h
Normal file
544
drivers/net/ethernet/ti/tlan.h
Normal file
|
@ -0,0 +1,544 @@
|
|||
#ifndef TLAN_H
|
||||
#define TLAN_H
|
||||
/********************************************************************
|
||||
*
|
||||
* Linux ThunderLAN Driver
|
||||
*
|
||||
* tlan.h
|
||||
* by James Banks
|
||||
*
|
||||
* (C) 1997-1998 Caldera, Inc.
|
||||
* (C) 1999-2001 Torben Mathiasen
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*
|
||||
* Dec 10, 1999 Torben Mathiasen <torben.mathiasen@compaq.com>
|
||||
* New Maintainer
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* TLan Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_MIN_FRAME_SIZE 64
|
||||
#define TLAN_MAX_FRAME_SIZE 1600
|
||||
|
||||
#define TLAN_NUM_RX_LISTS 32
|
||||
#define TLAN_NUM_TX_LISTS 64
|
||||
|
||||
#define TLAN_IGNORE 0
|
||||
#define TLAN_RECORD 1
|
||||
|
||||
#define TLAN_DBG(lvl, format, args...) \
|
||||
do { \
|
||||
if (debug&lvl) \
|
||||
printk(KERN_DEBUG "TLAN: " format, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define TLAN_DEBUG_GNRL 0x0001
|
||||
#define TLAN_DEBUG_TX 0x0002
|
||||
#define TLAN_DEBUG_RX 0x0004
|
||||
#define TLAN_DEBUG_LIST 0x0008
|
||||
#define TLAN_DEBUG_PROBE 0x0010
|
||||
|
||||
#define TX_TIMEOUT (10*HZ) /* We need time for auto-neg */
|
||||
#define MAX_TLAN_BOARDS 8 /* Max number of boards installed
|
||||
at a time */
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* Device Identification Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012
|
||||
#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030
|
||||
#ifndef PCI_DEVICE_ID_OLICOM_OC2183
|
||||
#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
|
||||
#endif
|
||||
#ifndef PCI_DEVICE_ID_OLICOM_OC2325
|
||||
#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
|
||||
#endif
|
||||
#ifndef PCI_DEVICE_ID_OLICOM_OC2326
|
||||
#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
|
||||
#endif
|
||||
|
||||
struct tlan_adapter_entry {
|
||||
u16 vendor_id;
|
||||
u16 device_id;
|
||||
char *device_label;
|
||||
u32 flags;
|
||||
u16 addr_ofs;
|
||||
};
|
||||
|
||||
#define TLAN_ADAPTER_NONE 0x00000000
|
||||
#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
|
||||
#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
|
||||
#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
|
||||
#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
|
||||
|
||||
#define TLAN_SPEED_DEFAULT 0
|
||||
#define TLAN_SPEED_10 10
|
||||
#define TLAN_SPEED_100 100
|
||||
|
||||
#define TLAN_DUPLEX_DEFAULT 0
|
||||
#define TLAN_DUPLEX_HALF 1
|
||||
#define TLAN_DUPLEX_FULL 2
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* EISA Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define EISA_ID 0xc80 /* EISA ID Registers */
|
||||
#define EISA_ID0 0xc80 /* EISA ID Register 0 */
|
||||
#define EISA_ID1 0xc81 /* EISA ID Register 1 */
|
||||
#define EISA_ID2 0xc82 /* EISA ID Register 2 */
|
||||
#define EISA_ID3 0xc83 /* EISA ID Register 3 */
|
||||
#define EISA_CR 0xc84 /* EISA Control Register */
|
||||
#define EISA_REG0 0xc88 /* EISA Configuration Register 0 */
|
||||
#define EISA_REG1 0xc89 /* EISA Configuration Register 1 */
|
||||
#define EISA_REG2 0xc8a /* EISA Configuration Register 2 */
|
||||
#define EISA_REG3 0xc8f /* EISA Configuration Register 3 */
|
||||
#define EISA_APROM 0xc90 /* Ethernet Address PROM */
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* Rx/Tx List Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_BUFFERS_PER_LIST 10
|
||||
#define TLAN_LAST_BUFFER 0x80000000
|
||||
#define TLAN_CSTAT_UNUSED 0x8000
|
||||
#define TLAN_CSTAT_FRM_CMP 0x4000
|
||||
#define TLAN_CSTAT_READY 0x3000
|
||||
#define TLAN_CSTAT_EOC 0x0800
|
||||
#define TLAN_CSTAT_RX_ERROR 0x0400
|
||||
#define TLAN_CSTAT_PASS_CRC 0x0200
|
||||
#define TLAN_CSTAT_DP_PR 0x0100
|
||||
|
||||
|
||||
struct tlan_buffer {
|
||||
u32 count;
|
||||
u32 address;
|
||||
};
|
||||
|
||||
|
||||
struct tlan_list {
|
||||
u32 forward;
|
||||
u16 c_stat;
|
||||
u16 frame_size;
|
||||
struct tlan_buffer buffer[TLAN_BUFFERS_PER_LIST];
|
||||
};
|
||||
|
||||
|
||||
typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* PHY definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_PHY_MAX_ADDR 0x1F
|
||||
#define TLAN_PHY_NONE 0x20
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* TLAN Private Information Structure
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
struct tlan_priv {
|
||||
struct net_device *next_device;
|
||||
struct pci_dev *pci_dev;
|
||||
struct net_device *dev;
|
||||
void *dma_storage;
|
||||
dma_addr_t dma_storage_dma;
|
||||
unsigned int dma_size;
|
||||
u8 *pad_buffer;
|
||||
struct tlan_list *rx_list;
|
||||
dma_addr_t rx_list_dma;
|
||||
u8 *rx_buffer;
|
||||
dma_addr_t rx_buffer_dma;
|
||||
u32 rx_head;
|
||||
u32 rx_tail;
|
||||
u32 rx_eoc_count;
|
||||
struct tlan_list *tx_list;
|
||||
dma_addr_t tx_list_dma;
|
||||
u8 *tx_buffer;
|
||||
dma_addr_t tx_buffer_dma;
|
||||
u32 tx_head;
|
||||
u32 tx_in_progress;
|
||||
u32 tx_tail;
|
||||
u32 tx_busy_count;
|
||||
u32 phy_online;
|
||||
u32 timer_set_at;
|
||||
u32 timer_type;
|
||||
struct timer_list timer;
|
||||
struct timer_list media_timer;
|
||||
struct board *adapter;
|
||||
u32 adapter_rev;
|
||||
u32 aui;
|
||||
u32 debug;
|
||||
u32 duplex;
|
||||
u32 phy[2];
|
||||
u32 phy_num;
|
||||
u32 speed;
|
||||
u8 tlan_rev;
|
||||
u8 tlan_full_duplex;
|
||||
spinlock_t lock;
|
||||
struct work_struct tlan_tqueue;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* TLan Driver Timer Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_TIMER_ACTIVITY 2
|
||||
#define TLAN_TIMER_PHY_PDOWN 3
|
||||
#define TLAN_TIMER_PHY_PUP 4
|
||||
#define TLAN_TIMER_PHY_RESET 5
|
||||
#define TLAN_TIMER_PHY_START_LINK 6
|
||||
#define TLAN_TIMER_PHY_FINISH_AN 7
|
||||
#define TLAN_TIMER_FINISH_RESET 8
|
||||
|
||||
#define TLAN_TIMER_ACT_DELAY (HZ/10)
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* TLan Driver Eeprom Definitions
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_EEPROM_ACK 0
|
||||
#define TLAN_EEPROM_STOP 1
|
||||
|
||||
#define TLAN_EEPROM_SIZE 256
|
||||
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
* Host Register Offsets and Contents
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#define TLAN_HOST_CMD 0x00
|
||||
#define TLAN_HC_GO 0x80000000
|
||||
#define TLAN_HC_STOP 0x40000000
|
||||
#define TLAN_HC_ACK 0x20000000
|
||||
#define TLAN_HC_CS_MASK 0x1FE00000
|
||||
#define TLAN_HC_EOC 0x00100000
|
||||
#define TLAN_HC_RT 0x00080000
|
||||
#define TLAN_HC_NES 0x00040000
|
||||
#define TLAN_HC_AD_RST 0x00008000
|
||||
#define TLAN_HC_LD_TMR 0x00004000
|
||||
#define TLAN_HC_LD_THR 0x00002000
|
||||
#define TLAN_HC_REQ_INT 0x00001000
|
||||
#define TLAN_HC_INT_OFF 0x00000800
|
||||
#define TLAN_HC_INT_ON 0x00000400
|
||||
#define TLAN_HC_AC_MASK 0x000000FF
|
||||
#define TLAN_CH_PARM 0x04
|
||||
#define TLAN_DIO_ADR 0x08
|
||||
#define TLAN_DA_ADR_INC 0x8000
|
||||
#define TLAN_DA_RAM_ADR 0x4000
|
||||
#define TLAN_HOST_INT 0x0A
|
||||
#define TLAN_HI_IV_MASK 0x1FE0
|
||||
#define TLAN_HI_IT_MASK 0x001C
|
||||
#define TLAN_DIO_DATA 0x0C
|
||||
|
||||
|
||||
/* ThunderLAN Internal Register DIO Offsets */
|
||||
|
||||
#define TLAN_NET_CMD 0x00
|
||||
#define TLAN_NET_CMD_NRESET 0x80
|
||||
#define TLAN_NET_CMD_NWRAP 0x40
|
||||
#define TLAN_NET_CMD_CSF 0x20
|
||||
#define TLAN_NET_CMD_CAF 0x10
|
||||
#define TLAN_NET_CMD_NOBRX 0x08
|
||||
#define TLAN_NET_CMD_DUPLEX 0x04
|
||||
#define TLAN_NET_CMD_TRFRAM 0x02
|
||||
#define TLAN_NET_CMD_TXPACE 0x01
|
||||
#define TLAN_NET_SIO 0x01
|
||||
#define TLAN_NET_SIO_MINTEN 0x80
|
||||
#define TLAN_NET_SIO_ECLOK 0x40
|
||||
#define TLAN_NET_SIO_ETXEN 0x20
|
||||
#define TLAN_NET_SIO_EDATA 0x10
|
||||
#define TLAN_NET_SIO_NMRST 0x08
|
||||
#define TLAN_NET_SIO_MCLK 0x04
|
||||
#define TLAN_NET_SIO_MTXEN 0x02
|
||||
#define TLAN_NET_SIO_MDATA 0x01
|
||||
#define TLAN_NET_STS 0x02
|
||||
#define TLAN_NET_STS_MIRQ 0x80
|
||||
#define TLAN_NET_STS_HBEAT 0x40
|
||||
#define TLAN_NET_STS_TXSTOP 0x20
|
||||
#define TLAN_NET_STS_RXSTOP 0x10
|
||||
#define TLAN_NET_STS_RSRVD 0x0F
|
||||
#define TLAN_NET_MASK 0x03
|
||||
#define TLAN_NET_MASK_MASK7 0x80
|
||||
#define TLAN_NET_MASK_MASK6 0x40
|
||||
#define TLAN_NET_MASK_MASK5 0x20
|
||||
#define TLAN_NET_MASK_MASK4 0x10
|
||||
#define TLAN_NET_MASK_RSRVD 0x0F
|
||||
#define TLAN_NET_CONFIG 0x04
|
||||
#define TLAN_NET_CFG_RCLK 0x8000
|
||||
#define TLAN_NET_CFG_TCLK 0x4000
|
||||
#define TLAN_NET_CFG_BIT 0x2000
|
||||
#define TLAN_NET_CFG_RXCRC 0x1000
|
||||
#define TLAN_NET_CFG_PEF 0x0800
|
||||
#define TLAN_NET_CFG_1FRAG 0x0400
|
||||
#define TLAN_NET_CFG_1CHAN 0x0200
|
||||
#define TLAN_NET_CFG_MTEST 0x0100
|
||||
#define TLAN_NET_CFG_PHY_EN 0x0080
|
||||
#define TLAN_NET_CFG_MSMASK 0x007F
|
||||
#define TLAN_MAN_TEST 0x06
|
||||
#define TLAN_DEF_VENDOR_ID 0x08
|
||||
#define TLAN_DEF_DEVICE_ID 0x0A
|
||||
#define TLAN_DEF_REVISION 0x0C
|
||||
#define TLAN_DEF_SUBCLASS 0x0D
|
||||
#define TLAN_DEF_MIN_LAT 0x0E
|
||||
#define TLAN_DEF_MAX_LAT 0x0F
|
||||
#define TLAN_AREG_0 0x10
|
||||
#define TLAN_AREG_1 0x16
|
||||
#define TLAN_AREG_2 0x1C
|
||||
#define TLAN_AREG_3 0x22
|
||||
#define TLAN_HASH_1 0x28
|
||||
#define TLAN_HASH_2 0x2C
|
||||
#define TLAN_GOOD_TX_FRMS 0x30
|
||||
#define TLAN_TX_UNDERUNS 0x33
|
||||
#define TLAN_GOOD_RX_FRMS 0x34
|
||||
#define TLAN_RX_OVERRUNS 0x37
|
||||
#define TLAN_DEFERRED_TX 0x38
|
||||
#define TLAN_CRC_ERRORS 0x3A
|
||||
#define TLAN_CODE_ERRORS 0x3B
|
||||
#define TLAN_MULTICOL_FRMS 0x3C
|
||||
#define TLAN_SINGLECOL_FRMS 0x3E
|
||||
#define TLAN_EXCESSCOL_FRMS 0x40
|
||||
#define TLAN_LATE_COLS 0x41
|
||||
#define TLAN_CARRIER_LOSS 0x42
|
||||
#define TLAN_ACOMMIT 0x43
|
||||
#define TLAN_LED_REG 0x44
|
||||
#define TLAN_LED_ACT 0x10
|
||||
#define TLAN_LED_LINK 0x01
|
||||
#define TLAN_BSIZE_REG 0x45
|
||||
#define TLAN_MAX_RX 0x46
|
||||
#define TLAN_INT_DIS 0x48
|
||||
#define TLAN_ID_TX_EOC 0x04
|
||||
#define TLAN_ID_RX_EOF 0x02
|
||||
#define TLAN_ID_RX_EOC 0x01
|
||||
|
||||
|
||||
|
||||
/* ThunderLAN Interrupt Codes */
|
||||
|
||||
#define TLAN_INT_NUMBER_OF_INTS 8
|
||||
|
||||
#define TLAN_INT_NONE 0x0000
|
||||
#define TLAN_INT_TX_EOF 0x0001
|
||||
#define TLAN_INT_STAT_OVERFLOW 0x0002
|
||||
#define TLAN_INT_RX_EOF 0x0003
|
||||
#define TLAN_INT_DUMMY 0x0004
|
||||
#define TLAN_INT_TX_EOC 0x0005
|
||||
#define TLAN_INT_STATUS_CHECK 0x0006
|
||||
#define TLAN_INT_RX_EOC 0x0007
|
||||
|
||||
|
||||
|
||||
/* ThunderLAN MII Registers */
|
||||
|
||||
/* Generic MII/PHY Registers */
|
||||
|
||||
#define MII_GEN_CTL 0x00
|
||||
#define MII_GC_RESET 0x8000
|
||||
#define MII_GC_LOOPBK 0x4000
|
||||
#define MII_GC_SPEEDSEL 0x2000
|
||||
#define MII_GC_AUTOENB 0x1000
|
||||
#define MII_GC_PDOWN 0x0800
|
||||
#define MII_GC_ISOLATE 0x0400
|
||||
#define MII_GC_AUTORSRT 0x0200
|
||||
#define MII_GC_DUPLEX 0x0100
|
||||
#define MII_GC_COLTEST 0x0080
|
||||
#define MII_GC_RESERVED 0x007F
|
||||
#define MII_GEN_STS 0x01
|
||||
#define MII_GS_100BT4 0x8000
|
||||
#define MII_GS_100BTXFD 0x4000
|
||||
#define MII_GS_100BTXHD 0x2000
|
||||
#define MII_GS_10BTFD 0x1000
|
||||
#define MII_GS_10BTHD 0x0800
|
||||
#define MII_GS_RESERVED 0x07C0
|
||||
#define MII_GS_AUTOCMPLT 0x0020
|
||||
#define MII_GS_RFLT 0x0010
|
||||
#define MII_GS_AUTONEG 0x0008
|
||||
#define MII_GS_LINK 0x0004
|
||||
#define MII_GS_JABBER 0x0002
|
||||
#define MII_GS_EXTCAP 0x0001
|
||||
#define MII_GEN_ID_HI 0x02
|
||||
#define MII_GEN_ID_LO 0x03
|
||||
#define MII_GIL_OUI 0xFC00
|
||||
#define MII_GIL_MODEL 0x03F0
|
||||
#define MII_GIL_REVISION 0x000F
|
||||
#define MII_AN_ADV 0x04
|
||||
#define MII_AN_LPA 0x05
|
||||
#define MII_AN_EXP 0x06
|
||||
|
||||
/* ThunderLAN Specific MII/PHY Registers */
|
||||
|
||||
#define TLAN_TLPHY_ID 0x10
|
||||
#define TLAN_TLPHY_CTL 0x11
|
||||
#define TLAN_TC_IGLINK 0x8000
|
||||
#define TLAN_TC_SWAPOL 0x4000
|
||||
#define TLAN_TC_AUISEL 0x2000
|
||||
#define TLAN_TC_SQEEN 0x1000
|
||||
#define TLAN_TC_MTEST 0x0800
|
||||
#define TLAN_TC_RESERVED 0x07F8
|
||||
#define TLAN_TC_NFEW 0x0004
|
||||
#define TLAN_TC_INTEN 0x0002
|
||||
#define TLAN_TC_TINT 0x0001
|
||||
#define TLAN_TLPHY_STS 0x12
|
||||
#define TLAN_TS_MINT 0x8000
|
||||
#define TLAN_TS_PHOK 0x4000
|
||||
#define TLAN_TS_POLOK 0x2000
|
||||
#define TLAN_TS_TPENERGY 0x1000
|
||||
#define TLAN_TS_RESERVED 0x0FFF
|
||||
#define TLAN_TLPHY_PAR 0x19
|
||||
#define TLAN_PHY_CIM_STAT 0x0020
|
||||
#define TLAN_PHY_SPEED_100 0x0040
|
||||
#define TLAN_PHY_DUPLEX_FULL 0x0080
|
||||
#define TLAN_PHY_AN_EN_STAT 0x0400
|
||||
|
||||
/* National Sem. & Level1 PHY id's */
|
||||
#define NAT_SEM_ID1 0x2000
|
||||
#define NAT_SEM_ID2 0x5C01
|
||||
#define LEVEL1_ID1 0x7810
|
||||
#define LEVEL1_ID2 0x0000
|
||||
|
||||
#define CIRC_INC(a, b) if (++a >= b) a = 0
|
||||
|
||||
/* Routines to access internal registers. */
|
||||
|
||||
static inline u8 tlan_dio_read8(u16 base_addr, u16 internal_addr)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
return inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline u16 tlan_dio_read16(u16 base_addr, u16 internal_addr)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
return inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline u32 tlan_dio_read32(u16 base_addr, u16 internal_addr)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
return inl(base_addr + TLAN_DIO_DATA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline void tlan_dio_write8(u16 base_addr, u16 internal_addr, u8 data)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline void tlan_dio_write16(u16 base_addr, u16 internal_addr, u16 data)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline void tlan_dio_write32(u16 base_addr, u16 internal_addr, u32 data)
|
||||
{
|
||||
outw(internal_addr, base_addr + TLAN_DIO_ADR);
|
||||
outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
|
||||
|
||||
}
|
||||
|
||||
#define tlan_clear_bit(bit, port) outb_p(inb_p(port) & ~bit, port)
|
||||
#define tlan_get_bit(bit, port) ((int) (inb_p(port) & bit))
|
||||
#define tlan_set_bit(bit, port) outb_p(inb_p(port) | bit, port)
|
||||
|
||||
/*
|
||||
* given 6 bytes, view them as 8 6-bit numbers and return the XOR of those
|
||||
* the code below is about seven times as fast as the original code
|
||||
*
|
||||
* The original code was:
|
||||
*
|
||||
* u32 xor(u32 a, u32 b) { return ((a && !b ) || (! a && b )); }
|
||||
*
|
||||
* #define XOR8(a, b, c, d, e, f, g, h) \
|
||||
* xor(a, xor(b, xor(c, xor(d, xor(e, xor(f, xor(g, h)) ) ) ) ) )
|
||||
* #define DA(a, bit) (( (u8) a[bit/8] ) & ( (u8) (1 << bit%8)) )
|
||||
*
|
||||
* hash = XOR8(DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24),
|
||||
* DA(a,30), DA(a,36), DA(a,42));
|
||||
* hash |= XOR8(DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25),
|
||||
* DA(a,31), DA(a,37), DA(a,43)) << 1;
|
||||
* hash |= XOR8(DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26),
|
||||
* DA(a,32), DA(a,38), DA(a,44)) << 2;
|
||||
* hash |= XOR8(DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27),
|
||||
* DA(a,33), DA(a,39), DA(a,45)) << 3;
|
||||
* hash |= XOR8(DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28),
|
||||
* DA(a,34), DA(a,40), DA(a,46)) << 4;
|
||||
* hash |= XOR8(DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29),
|
||||
* DA(a,35), DA(a,41), DA(a,47)) << 5;
|
||||
*
|
||||
*/
|
||||
static inline u32 tlan_hash_func(const u8 *a)
|
||||
{
|
||||
u8 hash;
|
||||
|
||||
hash = (a[0]^a[3]); /* & 077 */
|
||||
hash ^= ((a[0]^a[3])>>6); /* & 003 */
|
||||
hash ^= ((a[1]^a[4])<<2); /* & 074 */
|
||||
hash ^= ((a[1]^a[4])>>4); /* & 017 */
|
||||
hash ^= ((a[2]^a[5])<<4); /* & 060 */
|
||||
hash ^= ((a[2]^a[5])>>2); /* & 077 */
|
||||
|
||||
return hash & 077;
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue