Support the CPTS as a PTP Hardware Clock
v3 cover Support the CPTS as a PTP Hardware Clock
This patch series adds support for the Common Platform Time Sync (CPTS) found on TI SoCs such as the am335x (like the popular BeagleBone) and the dm814x. The patch series enables hardware time stamping and a PTP Hardware Clock.
* Changes in V3
- Performs clock frequency adjustment in software
- Move the HWTSTAMP ioctl into the CPSW code
- Handle the two different CPSW versions
This work has been tested using the linuxptp PTP stack, and it seems to be working quite well. Using the CPSW Ethernet ports (and the CPTS) is not yet possible in mainline Linux due to missing device tree support. However, this support is expected to appear in v3.8 in the form of the following patches.
http://www.spinics.net/lists/arm-kernel/msg203749.html
This third version of the patch series incorporates ideas taken from Mugunthan's driver, namely the CPSW versioning support and keeping the HWTSTAMP ioctl within the CPSW code.
http://thread.gmane.org/gmane.linux.network/246082
The first patch adds multicast MAC support to the CPSW. Patches two and three are cleanups to bring the code more in line with the TRM. Patches four and five add CPSW versioning support. The sixth patch add the new CPTS code, and patches seven through nine add device tree support for CPTS variables. The last patch hooks the CPSW to the CPTS and adds HWTSTAMP support into the CPSW.
Thanks,
Richard
Mugunthan V N (1):
drivers: net: ethernet: cpsw: add multicast address to ALE table
Richard Cochran (9):
cpsw: rename register banks to match the reference manual
cpsw: add missing fields to the CPSW_SS register bank.
cpsw: remember the silicon version
cpsw: support both silicon versions
cpts: introduce time stamping code and a PTP hardware clock.
cpsw: add a DT field for the cpts offset
cpsw: add a DT field for the active time stamping port
cpts: specify the input clock frequency via DT
cpsw: support the HWTSTAMP ioctl and the CPTS
Documentation/devicetree/bindings/net/cpsw.txt | 20 +- drivers/net/ethernet/ti/Kconfig | 8 + drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/cpsw.c | 373 +++++++++++++++++++-- drivers/net/ethernet/ti/cpsw_ale.c | 31 ++- drivers/net/ethernet/ti/cpsw_ale.h | 1 + drivers/net/ethernet/ti/cpts.c | 427 ++++++++++++++++++++++++ drivers/net/ethernet/ti/cpts.h | 146 ++++++++ include/linux/platform_data/cpsw.h | 4 + 9 files changed, 979 insertions(+), 33 deletions(-) create mode 100644 drivers/net/ethernet/ti/cpts.c create mode 100644 drivers/net/ethernet/ti/cpts.h
Signature · 1 line
1.7.2.5
PATCH v3 1/10 drivers: net: ethernet: cpsw: add multicast address to ALE table
From: Mugunthan V N <[email protected]>
Adding multicast address to ALE table via netdev ops to subscribe, transmit or receive multicast frames to and from the network
--- drivers/net/ethernet/ti/cpsw.c | 27 +++++++++++++++++++++++++++ drivers/net/ethernet/ti/cpsw_ale.c | 31 ++++++++++++++++++++++++++++--- drivers/net/ethernet/ti/cpsw_ale.h | 1 + 3 files changed, 56 insertions(+), 3 deletions(-)
drivers/net/ethernet/ti/cpsw.c +27 -0
@@ -70,6 +70,8 @@ do { \ dev_notice(priv->dev, format, ## __VA_ARGS__); \ } while (0) +#define ALE_ALL_PORTS 0x7 +#define CPSW_MAJOR_VERSION (reg) (reg >> 8 & 0x7)#define CPSW_MINOR_VERSION (reg) (reg & 0xff)#define CPSW_RTL_VERSION (reg) ((reg >> 11) & 0x1f)
@@ -228,6 +230,30 @@ struct cpsw_priv { (func)((priv)->slaves + idx, ##arg); \ } while (0) +static void cpsw_ndo_set_rx_mode (struct net_device *ndev) +{ +struct cpsw_priv *priv =netdev_priv (ndev); + +if (ndev->flags &IFF_PROMISC ) { +/* Enable promiscuous mode */ +dev_err (priv->dev,"Ignoring Promiscuous mode\n" ); +return ; + } + +/* Clear all mcast from ALE */ +cpsw_ale_flush_multicast (priv->ale,ALE_ALL_PORTS << priv->host_port); + +if (!netdev_mc_empty (ndev)) { +struct netdev_hw_addr *ha; + +/* program multicast address list into ALE register */ +netdev_for_each_mc_addr (ha, ndev) { +cpsw_ale_add_mcast (priv->ale, (u8 *)ha->addr, +ALE_ALL_PORTS << priv->host_port,0 ,0 ); + } + } +} +static void cpsw_intr_enable (struct cpsw_priv *priv) {__raw_writel (0xFF , &priv->ss_regs->tx_en);
@@ -673,6 +699,7 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_change_mtu = eth_change_mtu, .ndo_tx_timeout = cpsw_ndo_tx_timeout, .ndo_get_stats = cpsw_ndo_get_stats, + .ndo_set_rx_mode = cpsw_ndo_set_rx_mode,#ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cpsw_ndo_poll_controller,#endif
drivers/net/ethernet/ti/cpsw_ale.c +28 -3
@@ -20,6 +20,7 @@#include <linux/io.h> #include <linux/stat.h> #include <linux/sysfs.h> +#include <linux/etherdevice.h> #include "cpsw_ale.h"
@@ -211,10 +212,34 @@ static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry, mask &= ~port_mask;/* free if only remaining port is host port */ -if (mask ==BIT (ale->params.ale_ports)) -cpsw_ale_set_entry_type (ale_entry,ALE_TYPE_FREE ); -else +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) +{ +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)) { +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,
drivers/net/ethernet/ti/cpsw_ale.h +1 -0
@@ -80,6 +80,7 @@ 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 cpsw_ale_add_ucast (struct cpsw_ale *ale,u8 *addr,int port,int flags);int cpsw_ale_del_ucast (struct cpsw_ale *ale,u8 *addr,int port); int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
Signature · 1 line
1.7.2.5
PATCH v3 2/10 cpsw: rename register banks to match the reference manual
In reply to Richard CochranThe code mixes up the CPSW_SS and the CPSW_WR register naming. This patch changes the names to conform to the published Technical Reference Manual from TI, in order to make working on the code less confusing.
--- drivers/net/ethernet/ti/cpsw.c | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-)
drivers/net/ethernet/ti/cpsw.c +9 -9
@@ -131,7 +131,7 @@ static int rx_packet_max = CPSW_MAX_PACKET_SIZE;module_param (rx_packet_max, int,0 );MODULE_PARM_DESC (rx_packet_max,"maximum receive packet size (bytes)" ); -struct cpsw_ss_regs { +struct cpsw_wr_regs {u32 id_ver;u32 soft_reset;u32 control;
@@ -142,7 +142,7 @@ struct cpsw_ss_regs {u32 misc_en; }; -struct cpsw_regs { +struct cpsw_ss_regs {u32 id_ver;u32 control;u32 soft_reset;
@@ -204,8 +204,8 @@ struct cpsw_priv {struct napi_struct napi;struct device *dev;struct cpsw_platform_data data; -struct cpsw_regs __iomem *regs; -struct cpsw_ss_regs __iomem *ss_regs; +struct cpsw_ss_regs __iomem *regs; +struct cpsw_wr_regs __iomem *wr_regs;struct cpsw_host_regs __iomem *host_port_regs;u32 msg_enable;struct net_device_stats stats;
@@ -256,8 +256,8 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)static void cpsw_intr_enable (struct cpsw_priv *priv) { -__raw_writel (0xFF , &priv->ss_regs->tx_en); -__raw_writel (0xFF , &priv->ss_regs->rx_en); +__raw_writel (0xFF , &priv->wr_regs->tx_en); +__raw_writel (0xFF , &priv->wr_regs->rx_en);cpdma_ctlr_int_ctrl (priv->dma, true);return ;
@@ -265,8 +265,8 @@ static void cpsw_intr_enable(struct cpsw_priv *priv)static void cpsw_intr_disable (struct cpsw_priv *priv) { -__raw_writel (0 , &priv->ss_regs->tx_en); -__raw_writel (0 , &priv->ss_regs->rx_en); +__raw_writel (0 , &priv->wr_regs->tx_en); +__raw_writel (0 , &priv->wr_regs->rx_en);cpdma_ctlr_int_ctrl (priv->dma, false);return ;
@@ -999,7 +999,7 @@ static int __devinit cpsw_probe(struct platform_device *pdev)dev_err (priv->dev,"unable to map i/o region\n" ); goto clean_cpsw_ss_iores_ret; } - priv->ss_regs = regs; + priv->wr_regs = regs;for_each_slave (priv, cpsw_slave_init, priv);
Signature · 1 line
1.7.2.5
PATCH v3 3/10 cpsw: add missing fields to the CPSW_SS register bank.
In reply to Richard Cochran--- drivers/net/ethernet/ti/cpsw.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
drivers/net/ethernet/ti/cpsw.c +8 -0
@@ -148,6 +148,14 @@ struct cpsw_ss_regs {u32 soft_reset;u32 stat_port_en;u32 ptype; +u32 soft_idle; +u32 thru_rate; +u32 gap_thresh; +u32 tx_start_wds; +u32 flow_control; +u32 vlan_ltype; +u32 ts_ltype; +u32 dlr_ltype; };struct cpsw_slave_regs {
Signature · 1 line
1.7.2.5
PATCH v3 4/10 cpsw: remember the silicon version
In reply to Richard CochranThis patch lets the CPSW driver remember the version number in order to support the two different variants already in the wild.
--- drivers/net/ethernet/ti/cpsw.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
drivers/net/ethernet/ti/cpsw.c +4 -0
@@ -76,6 +76,8 @@ do { \#define CPSW_MINOR_VERSION (reg) (reg & 0xff)#define CPSW_RTL_VERSION (reg) ((reg >> 11) & 0x1f) +#define CPSW_VERSION_1 0x19010a +#define CPSW_VERSION_2 0x19010c#define CPDMA_RXTHRESH 0x0c0#define CPDMA_RXFREE 0x0e0#define CPDMA_TXHDP 0x00
@@ -216,6 +218,7 @@ struct cpsw_priv {struct cpsw_wr_regs __iomem *wr_regs;struct cpsw_host_regs __iomem *host_port_regs;u32 msg_enable; +u32 version;struct net_device_stats stats;int rx_packet_max;int host_port;
@@ -540,6 +543,7 @@ static int cpsw_ndo_open(struct net_device *ndev)pm_runtime_get_sync (&priv->pdev->dev); reg =__raw_readl (&priv->regs->id_ver); + priv->version = reg; dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n", CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg),
Signature · 1 line
1.7.2.5
PATCH v3 5/10 cpsw: support both silicon versions
In reply to Richard CochranThis patch fixes the cpsw driver to operate correctly with both the dm814x and the am335x versions of the switch hardware.
--- Documentation/devicetree/bindings/net/cpsw.txt | 8 +- drivers/net/ethernet/ti/cpsw.c | 106 ++++++++++++++++++++---- 2 files changed, 94 insertions(+), 20 deletions(-)
Documentation/devicetree/bindings/net/cpsw.txt +4 -4
@@ -59,14 +59,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave at 0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave at 1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */
@@ -93,14 +93,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave at 0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave at 1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */
drivers/net/ethernet/ti/cpsw.c +90 -16
136 lines not on this page · raw message
Signature · 1 line
1.7.2.5
PATCH v3 6/10 cpts: introduce time stamping code and a PTP hardware clock.
In reply to Richard CochranThis patch adds a driver for the CPTS that offers time stamping and a PTP hardware clock. Because some of the CPTS hardware variants (like the am335x) do not support frequency adjustment, we have implemented this in software by changing the multiplication factor of the timecounter.
--- drivers/net/ethernet/ti/Kconfig | 8 + drivers/net/ethernet/ti/Makefile | 2 +- drivers/net/ethernet/ti/cpts.c | 427 ++++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/ti/cpts.h | 146 +++++++++++++ 4 files changed, 582 insertions(+), 1 deletions(-) create mode 100644 drivers/net/ethernet/ti/cpts.c create mode 100644 drivers/net/ethernet/ti/cpts.h
drivers/net/ethernet/ti/Kconfig +8 -0
@@ -60,6 +60,14 @@ config TI_CPSW 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 && PTP_1588_CLOCK && !(TI_CPSW=y && PTP_1588_CLOCK=m) + ---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)
drivers/net/ethernet/ti/Makefile +1 -1
@@ -8,4 +8,4 @@ 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) += ti_cpsw.o -ti_cpsw-y := cpsw_ale.o cpsw.o +ti_cpsw-y := cpsw_ale.o cpsw.o cpts.o
drivers/net/ethernet/ti/cpts.c new file +427 -0
427 lines not on this page · raw message
drivers/net/ethernet/ti/cpts.h new file +146 -0
146 lines not on this page · raw message
Signature · 1 line
1.7.2.5
PATCH v3 7/10 cpsw: add a DT field for the cpts offset
In reply to Richard Cochran--- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+), 0 deletions(-)
Documentation/devicetree/bindings/net/cpsw.txt +3 -0
@@ -16,6 +16,7 @@ Required properties: - ale_entries : Specifies No of entries ALE can hold - host_port_reg_ofs : Specifies host port register offset - hw_stats_reg_ofs : Specifies hardware statistics register offset +- cpts_reg_ofs : Specifies the offset of the CPTS registers - bd_ram_ofs : Specifies internal desciptor RAM offset - bd_ram_size : Specifies internal descriptor RAM size - rx_descs : Specifies number of Rx descriptors
@@ -52,6 +53,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>;
@@ -86,6 +88,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>;
drivers/net/ethernet/ti/cpsw.c +7 -0
@@ -912,6 +912,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->hw_stats_reg_ofs = prop; +if (of_property_read_u32 (node,"cpts_reg_ofs" , &prop)) { +pr_err ("Missing cpts_reg_ofs property in the DT.\n" ); + ret = -EINVAL ; + goto error_ret; + } + data->cpts_reg_ofs = prop; +if (of_property_read_u32 (node,"bd_ram_ofs" , &prop)) {pr_err ("Missing bd_ram_ofs property in the DT.\n" ); ret = -EINVAL ;
include/linux/platform_data/cpsw.h +1 -0
@@ -41,6 +41,7 @@ struct cpsw_platform_data {u32 host_port_num;/* The port number for the host port */ u32 hw_stats_reg_ofs;/* cpsw hardware statistics counters */ +u32 cpts_reg_ofs;/* cpts registers */ u32 bd_ram_ofs;/* embedded buffer descriptor RAM offset*/ u32 bd_ram_size;/*buffer descriptor ram size */
Signature · 1 line
1.7.2.5
PATCH v3 8/10 cpsw: add a DT field for the active time stamping port
In reply to Richard CochranBecause time stamping on both external ports of the switch simultaneously is positively useless from the application's point of view, this patch provides a DT configuration method to choose the active port.
--- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+), 0 deletions(-)
Documentation/devicetree/bindings/net/cpsw.txt +3 -0
@@ -23,6 +23,7 @@ Required properties: - mac_control : Specifies Default MAC control register content for the specific platform - slaves : Specifies number for slaves +- cpts_active_slave : Specifies the slave to use for time stamping - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id
@@ -60,6 +61,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave at 0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>;
@@ -95,6 +97,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave at 0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>;
drivers/net/ethernet/ti/cpsw.c +7 -0
@@ -847,6 +847,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; +if (of_property_read_u32 (node,"cpts_active_slave" , &prop)) { +pr_err ("Missing cpts_active_slave property in the DT.\n" ); + ret = -EINVAL ; + goto error_ret; + } + data->cpts_active_slave = prop; + data->slave_data =kzalloc (sizeof (struct cpsw_slave_data ) * data->slaves,GFP_KERNEL );if (!data->slave_data) {
include/linux/platform_data/cpsw.h +1 -0
@@ -33,6 +33,7 @@ struct cpsw_platform_data {u32 slaves;/* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; +u32 cpts_active_slave;/* time stamping slave */ u32 ale_reg_ofs;/* address lookup engine reg offset */ u32 ale_entries;/* ale table size */
Signature · 1 line
1.7.2.5
PATCH v3 9/10 cpts: specify the input clock frequency via DT
In reply to Richard CochranThis patch adds a way to configure the CPTS input clock scaling factors via the device tree.
--- Documentation/devicetree/bindings/net/cpsw.txt | 6 ++++++ drivers/net/ethernet/ti/cpsw.c | 14 ++++++++++++++ include/linux/platform_data/cpsw.h | 2 ++ 3 files changed, 22 insertions(+), 0 deletions(-)
Documentation/devicetree/bindings/net/cpsw.txt +6 -0
@@ -24,6 +24,8 @@ Required properties: for the specific platform - slaves : Specifies number for slaves - cpts_active_slave : Specifies the slave to use for time stamping +- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds +- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id
@@ -62,6 +64,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave at 0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>;
@@ -98,6 +102,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave at 0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>;
drivers/net/ethernet/ti/cpsw.c +14 -0
@@ -854,6 +854,20 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->cpts_active_slave = prop; +if (of_property_read_u32 (node,"cpts_clock_mult" , &prop)) { +pr_err ("Missing cpts_clock_mult property in the DT.\n" ); + ret = -EINVAL ; + goto error_ret; + } + data->cpts_clock_mult = prop; + +if (of_property_read_u32 (node,"cpts_clock_shift" , &prop)) { +pr_err ("Missing cpts_clock_shift property in the DT.\n" ); + ret = -EINVAL ; + goto error_ret; + } + data->cpts_clock_shift = prop; + data->slave_data =kzalloc (sizeof (struct cpsw_slave_data ) * data->slaves,GFP_KERNEL );if (!data->slave_data) {
include/linux/platform_data/cpsw.h +2 -0
@@ -34,6 +34,8 @@ struct cpsw_platform_data {u32 slaves;/* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data;u32 cpts_active_slave;/* time stamping slave */ +u32 cpts_clock_mult;/* convert input clock ticks to nanoseconds */ +u32 cpts_clock_shift;/* convert input clock ticks to nanoseconds */ u32 ale_reg_ofs;/* address lookup engine reg offset */ u32 ale_entries;/* ale table size */
Signature · 1 line
1.7.2.5
PATCH v3 10/10 cpsw: support the HWTSTAMP ioctl and the CPTS
In reply to Richard CochranThis patch hooks into the CPTS code and adds support for the HWTSTAMP ioctl. The patch includes code for the CPSW version found in the dm814x even though the background device tree support for this board is still missing.
--- drivers/net/ethernet/ti/cpsw.c | 182 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 182 insertions(+), 0 deletions(-)
drivers/net/ethernet/ti/cpsw.c +182 -0
@@ -24,6 +24,7 @@#include <linux/if_ether.h> #include <linux/etherdevice.h> #include <linux/netdevice.h> +#include <linux/net_tstamp.h> #include <linux/phy.h> #include <linux/workqueue.h> #include <linux/delay.h>
@@ -35,6 +36,7 @@#include <linux/platform_data/cpsw.h> #include "cpsw_ale.h" +#include "cpts.h" #include "davinci_cpdma.h" #define CPSW_DEBUG (NETIF_MSG_HW | NETIF_MSG_WOL | \
@@ -229,6 +231,14 @@ struct cpsw_ss_regs {/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */ #define EVENT_MSG_BITS ((1<<0) | (1<<1) | (1<<2) | (1<<3)) +/* Bit definitions for the CPSW1_TS_CTL register */ +#define CPSW_V1_TS_RX_EN BIT(0) +#define CPSW_V1_TS_TX_EN BIT(4) +#define CPSW_V1_MSG_TYPE_OFS 16 + +/* Bit definitions for the CPSW1_TS_SEQ_LTYPE register */ +#define CPSW_V1_SEQ_ID_OFS_SHIFT 16 +struct cpsw_host_regs {u32 max_blks;u32 blk_cnt;
@@ -297,6 +307,7 @@ struct cpsw_priv {/* snapshot of IRQ numbers */ u32 irqs_table[4 ];u32 num_irqs; +struct cpts cpts; };#define napi_to_priv (napi) container_of(napi, struct cpsw_priv, napi)
@@ -357,6 +368,7 @@ void cpsw_tx_handler(void *token, int len, int status)if (unlikely (netif_queue_stopped (ndev)))netif_start_queue (ndev); +cpts_tx_timestamp (&priv->cpts, skb); priv->stats.tx_packets++; priv->stats.tx_bytes += len;dev_kfree_skb_any (skb);
@@ -377,6 +389,7 @@ void cpsw_rx_handler(void *token, int len, int status) }if (likely (status >=0 )) {skb_put (skb, len); +cpts_rx_timestamp (&priv->cpts, skb); skb->protocol =eth_type_trans (skb, ndev);netif_receive_skb (skb); priv->stats.rx_bytes += len;
@@ -704,6 +717,11 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,return NETDEV_TX_OK ; } +if (skb_shinfo (skb)->tx_flags &SKBTX_HW_TSTAMP && priv->cpts.tx_enable) +skb_shinfo (skb)->tx_flags |=SKBTX_IN_PROGRESS ; + +skb_tx_timestamp (skb); + ret =cpdma_chan_submit (priv->txch, skb, skb->data, skb->len,GFP_KERNEL );if (unlikely (ret !=0 )) {
@@ -741,6 +759,130 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags)dev_err (&ndev->dev,"multicast traffic cannot be filtered!\n" ); } +#ifdef CONFIG_TI_CPTS + +static void cpsw_hwtstamp_v1 (struct cpsw_priv *priv) +{ +struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; +u32 ts_en, seq_id; + +if (!priv->cpts.tx_enable && !priv->cpts.rx_enable) { +slave_write (slave,0 ,CPSW1_TS_CTL ); +return ; + } + + seq_id = (30 <<CPSW_V1_SEQ_ID_OFS_SHIFT ) |ETH_P_1588 ; + ts_en =EVENT_MSG_BITS <<CPSW_V1_MSG_TYPE_OFS ; + +if (priv->cpts.tx_enable) + ts_en |=CPSW_V1_TS_TX_EN ; + +if (priv->cpts.rx_enable) + ts_en |=CPSW_V1_TS_RX_EN ; + +slave_write (slave, ts_en,CPSW1_TS_CTL ); +slave_write (slave, seq_id,CPSW1_TS_SEQ_LTYPE ); +} + +static void cpsw_hwtstamp_v2 (struct cpsw_priv *priv) +{ +struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; +u32 ctrl, mtype; + + ctrl =slave_read (slave,CPSW2_CONTROL ); + ctrl &= ~CTRL_ALL_TS_MASK ; + +if (priv->cpts.tx_enable) + ctrl |=CTRL_TX_TS_BITS ; + +if (priv->cpts.rx_enable) + ctrl |=CTRL_RX_TS_BITS ; + + mtype = (30 <<TS_SEQ_ID_OFFSET_SHIFT ) |EVENT_MSG_BITS ; + +slave_write (slave, mtype,CPSW2_TS_SEQ_MTYPE ); +slave_write (slave, ctrl,CPSW2_CONTROL ); +__raw_writel (ETH_P_1588 , &priv->regs->ts_ltype); +} + +static int cpsw_hwtstamp_ioctl (struct cpsw_priv *priv,struct ifreq *ifr) +{ +struct cpts *cpts = &priv->cpts; +struct hwtstamp_config cfg; + +if (copy_from_user (&cfg, ifr->ifr_data,sizeof (cfg))) +return -EFAULT ; + +/* reserved for future extensions */ +if (cfg.flags) +return -EINVAL ; + +switch (cfg.tx_type) { +case HWTSTAMP_TX_OFF : + cpts->tx_enable =0 ; +break ; +case HWTSTAMP_TX_ON : + cpts->tx_enable =1 ; +break ; +default : +return -ERANGE ; + } + +switch (cfg.rx_filter) { +case HWTSTAMP_FILTER_NONE : + cpts->rx_enable =0 ; +break ; +case HWTSTAMP_FILTER_ALL : +case HWTSTAMP_FILTER_PTP_V1_L4_EVENT : +case HWTSTAMP_FILTER_PTP_V1_L4_SYNC : +case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ : +return -ERANGE ; +case HWTSTAMP_FILTER_PTP_V2_L4_EVENT : +case HWTSTAMP_FILTER_PTP_V2_L4_SYNC : +case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ : +case HWTSTAMP_FILTER_PTP_V2_L2_EVENT : +case HWTSTAMP_FILTER_PTP_V2_L2_SYNC : +case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ : +case HWTSTAMP_FILTER_PTP_V2_EVENT : +case HWTSTAMP_FILTER_PTP_V2_SYNC : +case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ : + cpts->rx_enable =1 ; + cfg.rx_filter =HWTSTAMP_FILTER_PTP_V2_EVENT ; +break ; +default : +return -ERANGE ; + } + +switch (priv->version) { +case CPSW_VERSION_1 : +cpsw_hwtstamp_v1 (priv); +break ; +case CPSW_VERSION_2 : +cpsw_hwtstamp_v2 (priv); +break ; +default : +return -ENOTSUPP ; + } + +return copy_to_user (ifr->ifr_data, &cfg,sizeof (cfg)) ? -EFAULT :0 ; +} + +#endif /*CONFIG_TI_CPTS*/ + +static int cpsw_ndo_ioctl (struct net_device *dev,struct ifreq *req,int cmd) +{ +struct cpsw_priv *priv =netdev_priv (dev); + +if (!netif_running (dev)) +return -EINVAL ; + +#ifdef CONFIG_TI_CPTS +if (cmd ==SIOCSHWTSTAMP ) +return cpsw_hwtstamp_ioctl (priv, req); +#endif +return -ENOTSUPP ; +} +static void cpsw_ndo_tx_timeout (struct net_device *ndev) {struct cpsw_priv *priv =netdev_priv (ndev);
@@ -781,6 +923,7 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_stop = cpsw_ndo_stop, .ndo_start_xmit = cpsw_ndo_start_xmit, .ndo_change_rx_flags = cpsw_ndo_change_rx_flags, + .ndo_do_ioctl = cpsw_ndo_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = eth_change_mtu, .ndo_tx_timeout = cpsw_ndo_tx_timeout,
@@ -812,11 +955,44 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value) priv->msg_enable = value; } +static int cpsw_get_ts_info (struct net_device *ndev, +struct ethtool_ts_info *info) +{ +#ifdef CONFIG_TI_CPTS +struct cpsw_priv *priv =netdev_priv (ndev); + + info->so_timestamping = +SOF_TIMESTAMPING_TX_HARDWARE | +SOF_TIMESTAMPING_TX_SOFTWARE | +SOF_TIMESTAMPING_RX_HARDWARE | +SOF_TIMESTAMPING_RX_SOFTWARE | +SOF_TIMESTAMPING_SOFTWARE | +SOF_TIMESTAMPING_RAW_HARDWARE ; + info->phc_index = priv->cpts.phc_index; + info->tx_types = + (1 <<HWTSTAMP_TX_OFF ) | + (1 <<HWTSTAMP_TX_ON ); + info->rx_filters = + (1 <<HWTSTAMP_FILTER_NONE ) | + (1 <<HWTSTAMP_FILTER_PTP_V2_EVENT ); +#else + info->so_timestamping = +SOF_TIMESTAMPING_TX_SOFTWARE | +SOF_TIMESTAMPING_RX_SOFTWARE | +SOF_TIMESTAMPING_SOFTWARE ; + info->phc_index =-1 ; + info->tx_types =0 ; + info->rx_filters =0 ; +#endif +return 0 ; +} +static const struct ethtool_ops cpsw_ethtool_ops = { .get_drvinfo = cpsw_get_drvinfo, .get_msglevel = cpsw_get_msglevel, .set_msglevel = cpsw_set_msglevel, .get_link = ethtool_op_get_link, + .get_ts_info = cpsw_get_ts_info, };static void cpsw_slave_init (struct cpsw_slave *slave,struct cpsw_priv *priv)
@@ -1092,6 +1268,7 @@ static int __devinit cpsw_probe(struct platform_device *pdev) priv->regs = regs; priv->host_port = data->host_port_num; priv->host_port_regs = regs + data->host_port_reg_ofs; + priv->cpts.reg = regs + data->cpts_reg_ofs; priv->cpsw_ss_res =platform_get_resource (pdev,IORESOURCE_MEM ,1 );if (!priv->cpsw_ss_res) {
@@ -1213,6 +1390,10 @@ static int __devinit cpsw_probe(struct platform_device *pdev) goto clean_irq_ret; } +if (cpts_register (&pdev->dev, &priv->cpts, + data->cpts_clock_mult, data->cpts_clock_shift)) +dev_err (priv->dev,"error registering cpts device\n" ); +cpsw_notice (priv, probe,"initialized device (regs %x, irq %d)\n" , priv->cpsw_res->start, ndev->irq);
@@ -1252,6 +1433,7 @@ static int __devexit cpsw_remove(struct platform_device *pdev)pr_info ("removing device" );platform_set_drvdata (pdev,NULL ); +cpts_unregister (&priv->cpts);free_irq (ndev->irq, priv);cpsw_ale_destroy (priv->ale);cpdma_chan_destroy (priv->txch);
Signature · 1 line
1.7.2.5
David Miller
maintainerAll applied to net-next
Ben made some changes today to how PTP/PPS is Kconfig'd etc. so please make sure your driver is in sync with that.
Thanks.