Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

File diff suppressed because it is too large Load diff

View 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");

File diff suppressed because it is too large Load diff

View 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__ */

View 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;
}
}

View 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

View 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
}

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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(&regs->user[0].access);
if ((reg & USERACCESS_GO) == 0)
return 0;
reg = __raw_readl(&regs->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(&regs->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(&regs->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");

File diff suppressed because it is too large Load diff

View 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