Commit 64e8de58 authored by David S. Miller's avatar David S. Miller

Merge branch 'ti-cpts-update-and-fixes'

Grygorii Strashko says:

====================
net: ethernet: ti: cpts: update and fixes

It is preparation series intended to clean up and optimize TI CPTS driver to
facilitate further integration with other TI's SoCs like Keystone 2.

Changes in v5:
- fixed copy paste error in cpts_release
- reworked cc.mult/shift and cc_mult initialization

Changes in v4:
- fixed build error in patch
  "net: ethernet: ti: cpts: clean up event list if event pool is empty"
- rebased on top of net-next

Changes in v3:
- patches reordered: fixes and small updates moved first
- added comments in code about cpts->cc_mult
- conversation range (maxsec) limited to 10sec

Changes in v2:
- patch "net: ethernet: ti: cpts: rework initialization/deinitialization"
  was split on 4 patches
- applied comments from Richard Cochran
- dropped patch
  "net: ethernet: ti: cpts: add return value to tx and rx timestamp funcitons"
- new patches added:
  "net: ethernet: ti: cpts: drop excessive writes to CTRL and INT_EN regs"
  and "clocksource: export the clocks_calc_mult_shift to use by timestamp code"

Links on prev versions:
v4: https://lkml.org/lkml/2016/12/6/496
v3: https://www.spinics.net/lists/devicetree/msg153474.html
v2: http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1282034.html
v1: http://www.spinics.net/lists/linux-omap/msg131925.html
====================
Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d3243aef 20138cf9
...@@ -20,8 +20,6 @@ Required properties: ...@@ -20,8 +20,6 @@ Required properties:
- slaves : Specifies number for slaves - slaves : Specifies number for slaves
- active_slave : Specifies the slave to use for time stamping, - active_slave : Specifies the slave to use for time stamping,
ethtool and SIOCGMIIPHY ethtool and SIOCGMIIPHY
- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds
- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds
Optional properties: Optional properties:
- ti,hwmods : Must be "cpgmac0" - ti,hwmods : Must be "cpgmac0"
...@@ -35,7 +33,11 @@ Optional properties: ...@@ -35,7 +33,11 @@ Optional properties:
For example in dra72x-evm, pcf gpio has to be For example in dra72x-evm, pcf gpio has to be
driven low so that cpsw slave 0 and phy data driven low so that cpsw slave 0 and phy data
lines are connected via mux. lines are connected via mux.
- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds
- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds
Mult and shift will be calculated basing on CPTS
rftclk frequency if both cpts_clock_shift and
cpts_clock_mult properties are not provided.
Slave Properties: Slave Properties:
Required properties: Required properties:
......
...@@ -74,7 +74,7 @@ config TI_CPSW ...@@ -74,7 +74,7 @@ config TI_CPSW
will be called cpsw. will be called cpsw.
config TI_CPTS config TI_CPTS
bool "TI Common Platform Time Sync (CPTS) Support" tristate "TI Common Platform Time Sync (CPTS) Support"
depends on TI_CPSW depends on TI_CPSW
select PTP_1588_CLOCK select PTP_1588_CLOCK
---help--- ---help---
......
...@@ -12,8 +12,9 @@ obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o ...@@ -12,8 +12,9 @@ obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o
obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o
obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o
obj-$(CONFIG_TI_CPSW_ALE) += cpsw_ale.o obj-$(CONFIG_TI_CPSW_ALE) += cpsw_ale.o
obj-$(CONFIG_TI_CPTS) += cpts.o
obj-$(CONFIG_TI_CPSW) += ti_cpsw.o obj-$(CONFIG_TI_CPSW) += ti_cpsw.o
ti_cpsw-y := cpsw.o cpts.o ti_cpsw-y := cpsw.o
obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o
keystone_netcp-y := netcp_core.o keystone_netcp-y := netcp_core.o
......
...@@ -1487,9 +1487,7 @@ static int cpsw_ndo_open(struct net_device *ndev) ...@@ -1487,9 +1487,7 @@ static int cpsw_ndo_open(struct net_device *ndev)
if (ret < 0) if (ret < 0)
goto err_cleanup; goto err_cleanup;
if (cpts_register(cpsw->dev, cpsw->cpts, if (cpts_register(cpsw->cpts))
cpsw->data.cpts_clock_mult,
cpsw->data.cpts_clock_shift))
dev_err(priv->dev, "error registering cpts device\n"); dev_err(priv->dev, "error registering cpts device\n");
} }
...@@ -1562,7 +1560,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, ...@@ -1562,7 +1560,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
} }
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
cpsw->cpts->tx_enable) cpts_is_tx_enabled(cpsw->cpts))
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
skb_tx_timestamp(skb); skb_tx_timestamp(skb);
...@@ -1594,14 +1592,15 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, ...@@ -1594,14 +1592,15 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
} }
#ifdef CONFIG_TI_CPTS #if IS_ENABLED(CONFIG_TI_CPTS)
static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
{ {
struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave]; struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
u32 ts_en, seq_id; u32 ts_en, seq_id;
if (!cpsw->cpts->tx_enable && !cpsw->cpts->rx_enable) { if (!cpts_is_tx_enabled(cpsw->cpts) &&
!cpts_is_rx_enabled(cpsw->cpts)) {
slave_write(slave, 0, CPSW1_TS_CTL); slave_write(slave, 0, CPSW1_TS_CTL);
return; return;
} }
...@@ -1609,10 +1608,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) ...@@ -1609,10 +1608,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588; seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS; ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
if (cpsw->cpts->tx_enable) if (cpts_is_tx_enabled(cpsw->cpts))
ts_en |= CPSW_V1_TS_TX_EN; ts_en |= CPSW_V1_TS_TX_EN;
if (cpsw->cpts->rx_enable) if (cpts_is_rx_enabled(cpsw->cpts))
ts_en |= CPSW_V1_TS_RX_EN; ts_en |= CPSW_V1_TS_RX_EN;
slave_write(slave, ts_en, CPSW1_TS_CTL); slave_write(slave, ts_en, CPSW1_TS_CTL);
...@@ -1635,20 +1634,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) ...@@ -1635,20 +1634,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
case CPSW_VERSION_2: case CPSW_VERSION_2:
ctrl &= ~CTRL_V2_ALL_TS_MASK; ctrl &= ~CTRL_V2_ALL_TS_MASK;
if (cpsw->cpts->tx_enable) if (cpts_is_tx_enabled(cpsw->cpts))
ctrl |= CTRL_V2_TX_TS_BITS; ctrl |= CTRL_V2_TX_TS_BITS;
if (cpsw->cpts->rx_enable) if (cpts_is_rx_enabled(cpsw->cpts))
ctrl |= CTRL_V2_RX_TS_BITS; ctrl |= CTRL_V2_RX_TS_BITS;
break; break;
case CPSW_VERSION_3: case CPSW_VERSION_3:
default: default:
ctrl &= ~CTRL_V3_ALL_TS_MASK; ctrl &= ~CTRL_V3_ALL_TS_MASK;
if (cpsw->cpts->tx_enable) if (cpts_is_tx_enabled(cpsw->cpts))
ctrl |= CTRL_V3_TX_TS_BITS; ctrl |= CTRL_V3_TX_TS_BITS;
if (cpsw->cpts->rx_enable) if (cpts_is_rx_enabled(cpsw->cpts))
ctrl |= CTRL_V3_RX_TS_BITS; ctrl |= CTRL_V3_RX_TS_BITS;
break; break;
} }
...@@ -1684,7 +1683,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) ...@@ -1684,7 +1683,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
switch (cfg.rx_filter) { switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE: case HWTSTAMP_FILTER_NONE:
cpts->rx_enable = 0; cpts_rx_enable(cpts, 0);
break; break;
case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_ALL:
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
...@@ -1700,14 +1699,14 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) ...@@ -1700,14 +1699,14 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
cpts->rx_enable = 1; cpts_rx_enable(cpts, 1);
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
break; break;
default: default:
return -ERANGE; return -ERANGE;
} }
cpts->tx_enable = cfg.tx_type == HWTSTAMP_TX_ON; cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON);
switch (cpsw->version) { switch (cpsw->version) {
case CPSW_VERSION_1: case CPSW_VERSION_1:
...@@ -1736,13 +1735,23 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) ...@@ -1736,13 +1735,23 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
cfg.flags = 0; cfg.flags = 0;
cfg.tx_type = cpts->tx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; cfg.tx_type = cpts_is_tx_enabled(cpts) ?
cfg.rx_filter = (cpts->rx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE); HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
} }
#else
static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
{
return -EOPNOTSUPP;
}
static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
{
return -EOPNOTSUPP;
}
#endif /*CONFIG_TI_CPTS*/ #endif /*CONFIG_TI_CPTS*/
static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
...@@ -1755,12 +1764,10 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ...@@ -1755,12 +1764,10 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
return -EINVAL; return -EINVAL;
switch (cmd) { switch (cmd) {
#ifdef CONFIG_TI_CPTS
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
return cpsw_hwtstamp_set(dev, req); return cpsw_hwtstamp_set(dev, req);
case SIOCGHWTSTAMP: case SIOCGHWTSTAMP:
return cpsw_hwtstamp_get(dev, req); return cpsw_hwtstamp_get(dev, req);
#endif
} }
if (!cpsw->slaves[slave_no].phy) if (!cpsw->slaves[slave_no].phy)
...@@ -2100,10 +2107,10 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value) ...@@ -2100,10 +2107,10 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value)
priv->msg_enable = value; priv->msg_enable = value;
} }
#if IS_ENABLED(CONFIG_TI_CPTS)
static int cpsw_get_ts_info(struct net_device *ndev, static int cpsw_get_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info) struct ethtool_ts_info *info)
{ {
#ifdef CONFIG_TI_CPTS
struct cpsw_common *cpsw = ndev_to_cpsw(ndev); struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
info->so_timestamping = info->so_timestamping =
...@@ -2120,7 +2127,12 @@ static int cpsw_get_ts_info(struct net_device *ndev, ...@@ -2120,7 +2127,12 @@ static int cpsw_get_ts_info(struct net_device *ndev,
info->rx_filters = info->rx_filters =
(1 << HWTSTAMP_FILTER_NONE) | (1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V2_EVENT); (1 << HWTSTAMP_FILTER_PTP_V2_EVENT);
return 0;
}
#else #else
static int cpsw_get_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info)
{
info->so_timestamping = info->so_timestamping =
SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
...@@ -2128,9 +2140,9 @@ static int cpsw_get_ts_info(struct net_device *ndev, ...@@ -2128,9 +2140,9 @@ static int cpsw_get_ts_info(struct net_device *ndev,
info->phc_index = -1; info->phc_index = -1;
info->tx_types = 0; info->tx_types = 0;
info->rx_filters = 0; info->rx_filters = 0;
#endif
return 0; return 0;
} }
#endif
static int cpsw_get_link_ksettings(struct net_device *ndev, static int cpsw_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *ecmd) struct ethtool_link_ksettings *ecmd)
...@@ -2512,18 +2524,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, ...@@ -2512,18 +2524,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
} }
data->active_slave = prop; data->active_slave = prop;
if (of_property_read_u32(node, "cpts_clock_mult", &prop)) {
dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n");
return -EINVAL;
}
data->cpts_clock_mult = prop;
if (of_property_read_u32(node, "cpts_clock_shift", &prop)) {
dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n");
return -EINVAL;
}
data->cpts_clock_shift = prop;
data->slave_data = devm_kzalloc(&pdev->dev, data->slaves data->slave_data = devm_kzalloc(&pdev->dev, data->slaves
* sizeof(struct cpsw_slave_data), * sizeof(struct cpsw_slave_data),
GFP_KERNEL); GFP_KERNEL);
...@@ -2782,6 +2782,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2782,6 +2782,7 @@ static int cpsw_probe(struct platform_device *pdev)
struct cpdma_params dma_params; struct cpdma_params dma_params;
struct cpsw_ale_params ale_params; struct cpsw_ale_params ale_params;
void __iomem *ss_regs; void __iomem *ss_regs;
void __iomem *cpts_regs;
struct resource *res, *ss_res; struct resource *res, *ss_res;
const struct of_device_id *of_id; const struct of_device_id *of_id;
struct gpio_descs *mode; struct gpio_descs *mode;
...@@ -2809,12 +2810,6 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2809,12 +2810,6 @@ static int cpsw_probe(struct platform_device *pdev)
priv->dev = &ndev->dev; priv->dev = &ndev->dev;
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
cpsw->rx_packet_max = max(rx_packet_max, 128); cpsw->rx_packet_max = max(rx_packet_max, 128);
cpsw->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL);
if (!cpsw->cpts) {
dev_err(&pdev->dev, "error allocating cpts\n");
ret = -ENOMEM;
goto clean_ndev_ret;
}
mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW); mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW);
if (IS_ERR(mode)) { if (IS_ERR(mode)) {
...@@ -2902,7 +2897,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2902,7 +2897,7 @@ static int cpsw_probe(struct platform_device *pdev)
switch (cpsw->version) { switch (cpsw->version) {
case CPSW_VERSION_1: case CPSW_VERSION_1:
cpsw->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; cpsw->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET;
cpsw->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; cpts_regs = ss_regs + CPSW1_CPTS_OFFSET;
cpsw->hw_stats = ss_regs + CPSW1_HW_STATS; cpsw->hw_stats = ss_regs + CPSW1_HW_STATS;
dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET;
dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET;
...@@ -2916,7 +2911,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2916,7 +2911,7 @@ static int cpsw_probe(struct platform_device *pdev)
case CPSW_VERSION_3: case CPSW_VERSION_3:
case CPSW_VERSION_4: case CPSW_VERSION_4:
cpsw->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; cpsw->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET;
cpsw->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; cpts_regs = ss_regs + CPSW2_CPTS_OFFSET;
cpsw->hw_stats = ss_regs + CPSW2_HW_STATS; cpsw->hw_stats = ss_regs + CPSW2_HW_STATS;
dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET;
dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET;
...@@ -2983,6 +2978,12 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2983,6 +2978,12 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_dma_ret; goto clean_dma_ret;
} }
cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpsw->dev->of_node);
if (IS_ERR(cpsw->cpts)) {
ret = PTR_ERR(cpsw->cpts);
goto clean_ale_ret;
}
ndev->irq = platform_get_irq(pdev, 1); ndev->irq = platform_get_irq(pdev, 1);
if (ndev->irq < 0) { if (ndev->irq < 0) {
dev_err(priv->dev, "error getting irq resource\n"); dev_err(priv->dev, "error getting irq resource\n");
...@@ -3098,6 +3099,7 @@ static int cpsw_remove(struct platform_device *pdev) ...@@ -3098,6 +3099,7 @@ static int cpsw_remove(struct platform_device *pdev)
unregister_netdev(cpsw->slaves[1].ndev); unregister_netdev(cpsw->slaves[1].ndev);
unregister_netdev(ndev); unregister_netdev(ndev);
cpts_release(cpsw->cpts);
cpsw_ale_destroy(cpsw->ale); cpsw_ale_destroy(cpsw->ale);
cpdma_ctlr_destroy(cpsw->dma); cpdma_ctlr_destroy(cpsw->dma);
cpsw_remove_dt(pdev); cpsw_remove_dt(pdev);
......
...@@ -31,8 +31,6 @@ struct cpsw_platform_data { ...@@ -31,8 +31,6 @@ struct cpsw_platform_data {
u32 channels; /* number of cpdma channels (symmetric) */ u32 channels; /* number of cpdma channels (symmetric) */
u32 slaves; /* number of slave cpgmac ports */ u32 slaves; /* number of slave cpgmac ports */
u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ 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 ale_entries; /* ale table size */
u32 bd_ram_size; /*buffer descriptor ram size */ u32 bd_ram_size; /*buffer descriptor ram size */
u32 mac_control; /* Mac control register */ u32 mac_control; /* Mac control register */
......
...@@ -31,10 +31,8 @@ ...@@ -31,10 +31,8 @@
#include "cpts.h" #include "cpts.h"
#ifdef CONFIG_TI_CPTS #define cpts_read32(c, r) readl_relaxed(&c->reg->r)
#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r)
#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) static int event_expired(struct cpts_event *event)
{ {
...@@ -59,6 +57,26 @@ static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) ...@@ -59,6 +57,26 @@ static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low)
return -1; return -1;
} }
static int cpts_purge_events(struct cpts *cpts)
{
struct list_head *this, *next;
struct cpts_event *event;
int removed = 0;
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);
++removed;
}
}
if (removed)
pr_debug("cpts: event pool cleaned up %d\n", removed);
return removed ? 0 : -1;
}
/* /*
* Returns zero if matching event type was found. * Returns zero if matching event type was found.
*/ */
...@@ -71,10 +89,12 @@ static int cpts_fifo_read(struct cpts *cpts, int match) ...@@ -71,10 +89,12 @@ static int cpts_fifo_read(struct cpts *cpts, int match)
for (i = 0; i < CPTS_FIFO_DEPTH; i++) { for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
if (cpts_fifo_pop(cpts, &hi, &lo)) if (cpts_fifo_pop(cpts, &hi, &lo))
break; break;
if (list_empty(&cpts->pool)) {
pr_err("cpts: event pool is empty\n"); if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) {
pr_err("cpts: event pool empty\n");
return -1; return -1;
} }
event = list_first_entry(&cpts->pool, struct cpts_event, list); event = list_first_entry(&cpts->pool, struct cpts_event, list);
event->tmo = jiffies + 2; event->tmo = jiffies + 2;
event->high = hi; event->high = hi;
...@@ -223,27 +243,9 @@ static void cpts_overflow_check(struct work_struct *work) ...@@ -223,27 +243,9 @@ static void cpts_overflow_check(struct work_struct *work)
struct timespec64 ts; struct timespec64 ts;
struct cpts *cpts = container_of(work, struct cpts, overflow_work.work); 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); cpts_ptp_gettime(&cpts->info, &ts);
pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec);
schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_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, static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
...@@ -334,6 +336,7 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) ...@@ -334,6 +336,7 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
memset(ssh, 0, sizeof(*ssh)); memset(ssh, 0, sizeof(*ssh));
ssh->hwtstamp = ns_to_ktime(ns); ssh->hwtstamp = ns_to_ktime(ns);
} }
EXPORT_SYMBOL_GPL(cpts_rx_timestamp);
void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
...@@ -349,60 +352,170 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) ...@@ -349,60 +352,170 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
ssh.hwtstamp = ns_to_ktime(ns); ssh.hwtstamp = ns_to_ktime(ns);
skb_tstamp_tx(skb, &ssh); skb_tstamp_tx(skb, &ssh);
} }
EXPORT_SYMBOL_GPL(cpts_tx_timestamp);
#endif /*CONFIG_TI_CPTS*/ int cpts_register(struct cpts *cpts)
int cpts_register(struct device *dev, struct cpts *cpts,
u32 mult, u32 shift)
{ {
#ifdef CONFIG_TI_CPTS
int err, i; 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->events);
INIT_LIST_HEAD(&cpts->pool); INIT_LIST_HEAD(&cpts->pool);
for (i = 0; i < CPTS_MAX_EVENTS; i++) for (i = 0; i < CPTS_MAX_EVENTS; i++)
list_add(&cpts->pool_data[i].list, &cpts->pool); list_add(&cpts->pool_data[i].list, &cpts->pool);
cpts_clk_init(dev, cpts); clk_enable(cpts->refclk);
cpts_write32(cpts, CPTS_EN, control); cpts_write32(cpts, CPTS_EN, control);
cpts_write32(cpts, TS_PEND_EN, int_enable); 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())); 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->clock = ptp_clock_register(&cpts->info, cpts->dev);
if (IS_ERR(cpts->clock)) {
err = PTR_ERR(cpts->clock);
cpts->clock = NULL;
goto err_ptp;
}
cpts->phc_index = ptp_clock_index(cpts->clock); cpts->phc_index = ptp_clock_index(cpts->clock);
#endif
schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period);
return 0; return 0;
err_ptp:
clk_disable(cpts->refclk);
return err;
} }
EXPORT_SYMBOL_GPL(cpts_register);
void cpts_unregister(struct cpts *cpts) void cpts_unregister(struct cpts *cpts)
{ {
#ifdef CONFIG_TI_CPTS if (WARN_ON(!cpts->clock))
if (cpts->clock) { return;
ptp_clock_unregister(cpts->clock);
cancel_delayed_work_sync(&cpts->overflow_work); cancel_delayed_work_sync(&cpts->overflow_work);
ptp_clock_unregister(cpts->clock);
cpts->clock = NULL;
cpts_write32(cpts, 0, int_enable);
cpts_write32(cpts, 0, control);
clk_disable(cpts->refclk);
}
EXPORT_SYMBOL_GPL(cpts_unregister);
static void cpts_calc_mult_shift(struct cpts *cpts)
{
u64 frac, maxsec, ns;
u32 freq;
freq = clk_get_rate(cpts->refclk);
/* Calc the maximum number of seconds which we can run before
* wrapping around.
*/
maxsec = cpts->cc.mask;
do_div(maxsec, freq);
/* limit conversation rate to 10 sec as higher values will produce
* too small mult factors and so reduce the conversion accuracy
*/
if (maxsec > 10)
maxsec = 10;
/* Calc overflow check period (maxsec / 2) */
cpts->ov_check_period = (HZ * maxsec) / 2;
dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n",
cpts->ov_check_period);
if (cpts->cc.mult || cpts->cc.shift)
return;
clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift,
freq, NSEC_PER_SEC, maxsec);
frac = 0;
ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac);
dev_info(cpts->dev,
"CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n",
freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC));
}
static int cpts_of_parse(struct cpts *cpts, struct device_node *node)
{
int ret = -EINVAL;
u32 prop;
if (!of_property_read_u32(node, "cpts_clock_mult", &prop))
cpts->cc.mult = prop;
if (!of_property_read_u32(node, "cpts_clock_shift", &prop))
cpts->cc.shift = prop;
if ((cpts->cc.mult && !cpts->cc.shift) ||
(!cpts->cc.mult && cpts->cc.shift))
goto of_error;
return 0;
of_error:
dev_err(cpts->dev, "CPTS: Missing property in the DT.\n");
return ret;
}
struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node)
{
struct cpts *cpts;
int ret;
cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL);
if (!cpts)
return ERR_PTR(-ENOMEM);
cpts->dev = dev;
cpts->reg = (struct cpsw_cpts __iomem *)regs;
spin_lock_init(&cpts->lock);
INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check);
ret = cpts_of_parse(cpts, node);
if (ret)
return ERR_PTR(ret);
cpts->refclk = devm_clk_get(dev, "cpts");
if (IS_ERR(cpts->refclk)) {
dev_err(dev, "Failed to get cpts refclk\n");
return ERR_PTR(PTR_ERR(cpts->refclk));
} }
if (cpts->refclk)
cpts_clk_release(cpts); clk_prepare(cpts->refclk);
#endif
cpts->cc.read = cpts_systim_read;
cpts->cc.mask = CLOCKSOURCE_MASK(32);
cpts->info = cpts_info;
cpts_calc_mult_shift(cpts);
/* save cc.mult original value as it can be modified
* by cpts_ptp_adjfreq().
*/
cpts->cc_mult = cpts->cc.mult;
return cpts;
} }
EXPORT_SYMBOL_GPL(cpts_create);
void cpts_release(struct cpts *cpts)
{
if (!cpts)
return;
if (WARN_ON(!cpts->refclk))
return;
clk_unprepare(cpts->refclk);
}
EXPORT_SYMBOL_GPL(cpts_release);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("TI CPTS driver");
MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
...@@ -20,11 +20,14 @@ ...@@ -20,11 +20,14 @@
#ifndef _TI_CPTS_H_ #ifndef _TI_CPTS_H_
#define _TI_CPTS_H_ #define _TI_CPTS_H_
#if IS_ENABLED(CONFIG_TI_CPTS)
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/of.h>
#include <linux/ptp_clock_kernel.h> #include <linux/ptp_clock_kernel.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/timecounter.h> #include <linux/timecounter.h>
...@@ -94,9 +97,6 @@ enum { ...@@ -94,9 +97,6 @@ enum {
CPTS_EV_TX, /* Ethernet Transmit 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_FIFO_DEPTH 16
#define CPTS_MAX_EVENTS 32 #define CPTS_MAX_EVENTS 32
...@@ -108,10 +108,10 @@ struct cpts_event { ...@@ -108,10 +108,10 @@ struct cpts_event {
}; };
struct cpts { struct cpts {
struct device *dev;
struct cpsw_cpts __iomem *reg; struct cpsw_cpts __iomem *reg;
int tx_enable; int tx_enable;
int rx_enable; int rx_enable;
#ifdef CONFIG_TI_CPTS
struct ptp_clock_info info; struct ptp_clock_info info;
struct ptp_clock *clock; struct ptp_clock *clock;
spinlock_t lock; /* protects time registers */ spinlock_t lock; /* protects time registers */
...@@ -124,22 +124,86 @@ struct cpts { ...@@ -124,22 +124,86 @@ struct cpts {
struct list_head events; struct list_head events;
struct list_head pool; struct list_head pool;
struct cpts_event pool_data[CPTS_MAX_EVENTS]; struct cpts_event pool_data[CPTS_MAX_EVENTS];
#endif unsigned long ov_check_period;
}; };
#ifdef CONFIG_TI_CPTS
void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
int cpts_register(struct cpts *cpts);
void cpts_unregister(struct cpts *cpts);
struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node);
void cpts_release(struct cpts *cpts);
static inline void cpts_rx_enable(struct cpts *cpts, int enable)
{
cpts->rx_enable = enable;
}
static inline bool cpts_is_rx_enabled(struct cpts *cpts)
{
return !!cpts->rx_enable;
}
static inline void cpts_tx_enable(struct cpts *cpts, int enable)
{
cpts->tx_enable = enable;
}
static inline bool cpts_is_tx_enabled(struct cpts *cpts)
{
return !!cpts->tx_enable;
}
#else #else
struct cpts;
static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 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) static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
} }
static inline
struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node)
{
return NULL;
}
static inline void cpts_release(struct cpts *cpts)
{
}
static inline int
cpts_register(struct cpts *cpts)
{
return 0;
}
static inline void cpts_unregister(struct cpts *cpts)
{
}
static inline void cpts_rx_enable(struct cpts *cpts, int enable)
{
}
static inline bool cpts_is_rx_enabled(struct cpts *cpts)
{
return false;
}
static inline void cpts_tx_enable(struct cpts *cpts, int enable)
{
}
static inline bool cpts_is_tx_enabled(struct cpts *cpts)
{
return false;
}
#endif #endif
int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift);
void cpts_unregister(struct cpts *cpts);
#endif #endif
...@@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) ...@@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
*mult = tmp; *mult = tmp;
*shift = sft; *shift = sft;
} }
EXPORT_SYMBOL_GPL(clocks_calc_mult_shift);
/*[Clocksource internal variables]--------- /*[Clocksource internal variables]---------
* curr_clocksource: * curr_clocksource:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment