mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 18:24:14 +08:00
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: Richard Cochran <richardcochran@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
64e8de58c5
@ -20,8 +20,6 @@ Required properties:
|
||||
- slaves : Specifies number for slaves
|
||||
- active_slave : Specifies the slave to use for time stamping,
|
||||
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:
|
||||
- ti,hwmods : Must be "cpgmac0"
|
||||
@ -35,7 +33,11 @@ Optional properties:
|
||||
For example in dra72x-evm, pcf gpio has to be
|
||||
driven low so that cpsw slave 0 and phy data
|
||||
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:
|
||||
Required properties:
|
||||
|
@ -74,7 +74,7 @@ config TI_CPSW
|
||||
will be called cpsw.
|
||||
|
||||
config TI_CPTS
|
||||
bool "TI Common Platform Time Sync (CPTS) Support"
|
||||
tristate "TI Common Platform Time Sync (CPTS) Support"
|
||||
depends on TI_CPSW
|
||||
select PTP_1588_CLOCK
|
||||
---help---
|
||||
|
@ -12,8 +12,9 @@ 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_ALE) += cpsw_ale.o
|
||||
obj-$(CONFIG_TI_CPTS) += cpts.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
|
||||
keystone_netcp-y := netcp_core.o
|
||||
|
@ -1487,9 +1487,7 @@ static int cpsw_ndo_open(struct net_device *ndev)
|
||||
if (ret < 0)
|
||||
goto err_cleanup;
|
||||
|
||||
if (cpts_register(cpsw->dev, cpsw->cpts,
|
||||
cpsw->data.cpts_clock_mult,
|
||||
cpsw->data.cpts_clock_shift))
|
||||
if (cpts_register(cpsw->cpts))
|
||||
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,
|
||||
}
|
||||
|
||||
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_tx_timestamp(skb);
|
||||
@ -1594,14 +1592,15 @@ fail:
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
#if IS_ENABLED(CONFIG_TI_CPTS)
|
||||
|
||||
static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
|
||||
{
|
||||
struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -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;
|
||||
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;
|
||||
|
||||
if (cpsw->cpts->rx_enable)
|
||||
if (cpts_is_rx_enabled(cpsw->cpts))
|
||||
ts_en |= CPSW_V1_TS_RX_EN;
|
||||
|
||||
slave_write(slave, ts_en, CPSW1_TS_CTL);
|
||||
@ -1635,20 +1634,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
|
||||
case CPSW_VERSION_2:
|
||||
ctrl &= ~CTRL_V2_ALL_TS_MASK;
|
||||
|
||||
if (cpsw->cpts->tx_enable)
|
||||
if (cpts_is_tx_enabled(cpsw->cpts))
|
||||
ctrl |= CTRL_V2_TX_TS_BITS;
|
||||
|
||||
if (cpsw->cpts->rx_enable)
|
||||
if (cpts_is_rx_enabled(cpsw->cpts))
|
||||
ctrl |= CTRL_V2_RX_TS_BITS;
|
||||
break;
|
||||
case CPSW_VERSION_3:
|
||||
default:
|
||||
ctrl &= ~CTRL_V3_ALL_TS_MASK;
|
||||
|
||||
if (cpsw->cpts->tx_enable)
|
||||
if (cpts_is_tx_enabled(cpsw->cpts))
|
||||
ctrl |= CTRL_V3_TX_TS_BITS;
|
||||
|
||||
if (cpsw->cpts->rx_enable)
|
||||
if (cpts_is_rx_enabled(cpsw->cpts))
|
||||
ctrl |= CTRL_V3_RX_TS_BITS;
|
||||
break;
|
||||
}
|
||||
@ -1684,7 +1683,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
|
||||
|
||||
switch (cfg.rx_filter) {
|
||||
case HWTSTAMP_FILTER_NONE:
|
||||
cpts->rx_enable = 0;
|
||||
cpts_rx_enable(cpts, 0);
|
||||
break;
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
@ -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_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||
cpts->rx_enable = 1;
|
||||
cpts_rx_enable(cpts, 1);
|
||||
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
cpts->tx_enable = cfg.tx_type == HWTSTAMP_TX_ON;
|
||||
cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON);
|
||||
|
||||
switch (cpsw->version) {
|
||||
case CPSW_VERSION_1:
|
||||
@ -1736,13 +1735,23 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
cfg.flags = 0;
|
||||
cfg.tx_type = cpts->tx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
|
||||
cfg.rx_filter = (cpts->rx_enable ?
|
||||
cfg.tx_type = cpts_is_tx_enabled(cpts) ?
|
||||
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
|
||||
cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
|
||||
HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE);
|
||||
|
||||
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*/
|
||||
|
||||
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;
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
case SIOCSHWTSTAMP:
|
||||
return cpsw_hwtstamp_set(dev, req);
|
||||
case SIOCGHWTSTAMP:
|
||||
return cpsw_hwtstamp_get(dev, req);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!cpsw->slaves[slave_no].phy)
|
||||
@ -2100,10 +2107,10 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value)
|
||||
priv->msg_enable = value;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_TI_CPTS)
|
||||
static int cpsw_get_ts_info(struct net_device *ndev,
|
||||
struct ethtool_ts_info *info)
|
||||
{
|
||||
#ifdef CONFIG_TI_CPTS
|
||||
struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
|
||||
|
||||
info->so_timestamping =
|
||||
@ -2120,7 +2127,12 @@ static int cpsw_get_ts_info(struct net_device *ndev,
|
||||
info->rx_filters =
|
||||
(1 << HWTSTAMP_FILTER_NONE) |
|
||||
(1 << HWTSTAMP_FILTER_PTP_V2_EVENT);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int cpsw_get_ts_info(struct net_device *ndev,
|
||||
struct ethtool_ts_info *info)
|
||||
{
|
||||
info->so_timestamping =
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_RX_SOFTWARE |
|
||||
@ -2128,9 +2140,9 @@ static int cpsw_get_ts_info(struct net_device *ndev,
|
||||
info->phc_index = -1;
|
||||
info->tx_types = 0;
|
||||
info->rx_filters = 0;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cpsw_get_link_ksettings(struct net_device *ndev,
|
||||
struct ethtool_link_ksettings *ecmd)
|
||||
@ -2512,18 +2524,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
|
||||
}
|
||||
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
|
||||
* sizeof(struct cpsw_slave_data),
|
||||
GFP_KERNEL);
|
||||
@ -2782,6 +2782,7 @@ static int cpsw_probe(struct platform_device *pdev)
|
||||
struct cpdma_params dma_params;
|
||||
struct cpsw_ale_params ale_params;
|
||||
void __iomem *ss_regs;
|
||||
void __iomem *cpts_regs;
|
||||
struct resource *res, *ss_res;
|
||||
const struct of_device_id *of_id;
|
||||
struct gpio_descs *mode;
|
||||
@ -2809,12 +2810,6 @@ static int cpsw_probe(struct platform_device *pdev)
|
||||
priv->dev = &ndev->dev;
|
||||
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
|
||||
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);
|
||||
if (IS_ERR(mode)) {
|
||||
@ -2902,7 +2897,7 @@ static int cpsw_probe(struct platform_device *pdev)
|
||||
switch (cpsw->version) {
|
||||
case CPSW_VERSION_1:
|
||||
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;
|
||||
dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET;
|
||||
dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET;
|
||||
@ -2916,7 +2911,7 @@ static int cpsw_probe(struct platform_device *pdev)
|
||||
case CPSW_VERSION_3:
|
||||
case CPSW_VERSION_4:
|
||||
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;
|
||||
dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET;
|
||||
dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET;
|
||||
@ -2983,6 +2978,12 @@ static int cpsw_probe(struct platform_device *pdev)
|
||||
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);
|
||||
if (ndev->irq < 0) {
|
||||
dev_err(priv->dev, "error getting irq resource\n");
|
||||
@ -3098,6 +3099,7 @@ static int cpsw_remove(struct platform_device *pdev)
|
||||
unregister_netdev(cpsw->slaves[1].ndev);
|
||||
unregister_netdev(ndev);
|
||||
|
||||
cpts_release(cpsw->cpts);
|
||||
cpsw_ale_destroy(cpsw->ale);
|
||||
cpdma_ctlr_destroy(cpsw->dma);
|
||||
cpsw_remove_dt(pdev);
|
||||
|
@ -31,8 +31,6 @@ struct cpsw_platform_data {
|
||||
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 mac_control; /* Mac control register */
|
||||
|
@ -31,10 +31,8 @@
|
||||
|
||||
#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)
|
||||
#define cpts_read32(c, r) readl_relaxed(&c->reg->r)
|
||||
#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r)
|
||||
|
||||
static int event_expired(struct cpts_event *event)
|
||||
{
|
||||
@ -59,6 +57,26 @@ static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low)
|
||||
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.
|
||||
*/
|
||||
@ -71,10 +89,12 @@ static int cpts_fifo_read(struct cpts *cpts, int match)
|
||||
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");
|
||||
|
||||
if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) {
|
||||
pr_err("cpts: event pool empty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = list_first_entry(&cpts->pool, struct cpts_event, list);
|
||||
event->tmo = jiffies + 2;
|
||||
event->high = hi;
|
||||
@ -223,27 +243,9 @@ static void cpts_overflow_check(struct work_struct *work)
|
||||
struct timespec64 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 %lld.%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);
|
||||
schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period);
|
||||
}
|
||||
|
||||
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)
|
||||
memset(ssh, 0, sizeof(*ssh));
|
||||
ssh->hwtstamp = ns_to_ktime(ns);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpts_rx_timestamp);
|
||||
|
||||
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);
|
||||
skb_tstamp_tx(skb, &ssh);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpts_tx_timestamp);
|
||||
|
||||
#endif /*CONFIG_TI_CPTS*/
|
||||
|
||||
int cpts_register(struct device *dev, struct cpts *cpts,
|
||||
u32 mult, u32 shift)
|
||||
int cpts_register(struct cpts *cpts)
|
||||
{
|
||||
#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);
|
||||
clk_enable(cpts->refclk);
|
||||
|
||||
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->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);
|
||||
#endif
|
||||
|
||||
schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period);
|
||||
return 0;
|
||||
|
||||
err_ptp:
|
||||
clk_disable(cpts->refclk);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpts_register);
|
||||
|
||||
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
|
||||
if (WARN_ON(!cpts->clock))
|
||||
return;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
clk_prepare(cpts->refclk);
|
||||
|
||||
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 @@
|
||||
#ifndef _TI_CPTS_H_
|
||||
#define _TI_CPTS_H_
|
||||
|
||||
#if IS_ENABLED(CONFIG_TI_CPTS)
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/timecounter.h>
|
||||
@ -94,9 +97,6 @@ enum {
|
||||
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
|
||||
|
||||
@ -108,10 +108,10 @@ struct cpts_event {
|
||||
};
|
||||
|
||||
struct cpts {
|
||||
struct device *dev;
|
||||
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 */
|
||||
@ -124,22 +124,86 @@ struct cpts {
|
||||
struct list_head events;
|
||||
struct list_head pool;
|
||||
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_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
|
||||
struct cpts;
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift);
|
||||
void cpts_unregister(struct cpts *cpts);
|
||||
|
||||
#endif
|
||||
|
@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
|
||||
*mult = tmp;
|
||||
*shift = sft;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clocks_calc_mult_shift);
|
||||
|
||||
/*[Clocksource internal variables]---------
|
||||
* curr_clocksource:
|
||||
|
Loading…
Reference in New Issue
Block a user