Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/xscale/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 6 kB image not shown  

Quelle  ptp_ixp46x.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * PTP 1588 clock using the IXP46X
 *
 * Copyright (C) 2010 OMICRON electronics GmbH
 */

#include <linux/device.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/platform_device.h>
#include <linux/soc/ixp4xx/cpu.h>

#include "ixp46x_ts.h"

#define DRIVER  "ptp_ixp46x"
#define N_EXT_TS 2

struct ixp_clock {
 struct ixp46x_ts_regs *regs;
 struct ptp_clock *ptp_clock;
 struct ptp_clock_info caps;
 int exts0_enabled;
 int exts1_enabled;
 int slave_irq;
 int master_irq;
};

static DEFINE_SPINLOCK(register_lock);

/*
 * Register access functions
 */


static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
{
 u64 ns;
 u32 lo, hi;

 lo = __raw_readl(®s->systime_lo);
 hi = __raw_readl(®s->systime_hi);

 ns = ((u64) hi) << 32;
 ns |= lo;
 ns <<= TICKS_NS_SHIFT;

 return ns;
}

static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
{
 u32 hi, lo;

 ns >>= TICKS_NS_SHIFT;
 hi = ns >> 32;
 lo = ns & 0xffffffff;

 __raw_writel(lo, ®s->systime_lo);
 __raw_writel(hi, ®s->systime_hi);
}

/*
 * Interrupt service routine
 */


static irqreturn_t isr(int irq, void *priv)
{
 struct ixp_clock *ixp_clock = priv;
 struct ixp46x_ts_regs *regs = ixp_clock->regs;
 struct ptp_clock_event event;
 u32 ack = 0, lo, hi, val;

 val = __raw_readl(®s->event);

 if (val & TSER_SNS) {
  ack |= TSER_SNS;
  if (ixp_clock->exts0_enabled) {
   hi = __raw_readl(®s->asms_hi);
   lo = __raw_readl(®s->asms_lo);
   event.type = PTP_CLOCK_EXTTS;
   event.index = 0;
   event.timestamp = ((u64) hi) << 32;
   event.timestamp |= lo;
   event.timestamp <<= TICKS_NS_SHIFT;
   ptp_clock_event(ixp_clock->ptp_clock, &event);
  }
 }

 if (val & TSER_SNM) {
  ack |= TSER_SNM;
  if (ixp_clock->exts1_enabled) {
   hi = __raw_readl(®s->amms_hi);
   lo = __raw_readl(®s->amms_lo);
   event.type = PTP_CLOCK_EXTTS;
   event.index = 1;
   event.timestamp = ((u64) hi) << 32;
   event.timestamp |= lo;
   event.timestamp <<= TICKS_NS_SHIFT;
   ptp_clock_event(ixp_clock->ptp_clock, &event);
  }
 }

 if (val & TTIPEND)
  ack |= TTIPEND; /* this bit seems to be always set */

 if (ack) {
  __raw_writel(ack, ®s->event);
  return IRQ_HANDLED;
 } else
  return IRQ_NONE;
}

/*
 * PTP clock operations
 */


static int ptp_ixp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
 u32 addend;
 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
 struct ixp46x_ts_regs *regs = ixp_clock->regs;

 addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm);

 __raw_writel(addend, ®s->addend);

 return 0;
}

static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
 s64 now;
 unsigned long flags;
 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
 struct ixp46x_ts_regs *regs = ixp_clock->regs;

 spin_lock_irqsave(®ister_lock, flags);

 now = ixp_systime_read(regs);
 now += delta;
 ixp_systime_write(regs, now);

 spin_unlock_irqrestore(®ister_lock, flags);

 return 0;
}

static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
 u64 ns;
 unsigned long flags;
 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
 struct ixp46x_ts_regs *regs = ixp_clock->regs;

 spin_lock_irqsave(®ister_lock, flags);

 ns = ixp_systime_read(regs);

 spin_unlock_irqrestore(®ister_lock, flags);

 *ts = ns_to_timespec64(ns);
 return 0;
}

static int ptp_ixp_settime(struct ptp_clock_info *ptp,
      const struct timespec64 *ts)
{
 u64 ns;
 unsigned long flags;
 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
 struct ixp46x_ts_regs *regs = ixp_clock->regs;

 ns = timespec64_to_ns(ts);

 spin_lock_irqsave(®ister_lock, flags);

 ixp_systime_write(regs, ns);

 spin_unlock_irqrestore(®ister_lock, flags);

 return 0;
}

static int ptp_ixp_enable(struct ptp_clock_info *ptp,
     struct ptp_clock_request *rq, int on)
{
 struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);

 switch (rq->type) {
 case PTP_CLK_REQ_EXTTS:
  switch (rq->extts.index) {
  case 0:
   ixp_clock->exts0_enabled = on ? 1 : 0;
   break;
  case 1:
   ixp_clock->exts1_enabled = on ? 1 : 0;
   break;
  default:
   return -EINVAL;
  }
  return 0;
 default:
  break;
 }

 return -EOPNOTSUPP;
}

static const struct ptp_clock_info ptp_ixp_caps = {
 .owner  = THIS_MODULE,
 .name  = "IXP46X timer",
 .max_adj = 66666655,
 .n_ext_ts = N_EXT_TS,
 .n_pins  = 0,
 .pps  = 0,
 .adjfine = ptp_ixp_adjfine,
 .adjtime = ptp_ixp_adjtime,
 .gettime64 = ptp_ixp_gettime,
 .settime64 = ptp_ixp_settime,
 .enable  = ptp_ixp_enable,
};

/* module operations */

static struct ixp_clock ixp_clock;

int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index)
{
 *regs = ixp_clock.regs;
 *phc_index = ptp_clock_index(ixp_clock.ptp_clock);

 if (!ixp_clock.ptp_clock)
  return -EPROBE_DEFER;

 return 0;
}
EXPORT_SYMBOL_GPL(ixp46x_ptp_find);

/* Called from the registered devm action */
static void ptp_ixp_unregister_action(void *d)
{
 struct ptp_clock *ptp_clock = d;

 ptp_clock_unregister(ptp_clock);
 ixp_clock.ptp_clock = NULL;
}

static int ptp_ixp_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 int ret;

 ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0);
 ixp_clock.master_irq = platform_get_irq(pdev, 0);
 ixp_clock.slave_irq = platform_get_irq(pdev, 1);
 if (IS_ERR(ixp_clock.regs) ||
     ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0)
  return -ENXIO;

 ixp_clock.caps = ptp_ixp_caps;

 ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);

 if (IS_ERR(ixp_clock.ptp_clock))
  return PTR_ERR(ixp_clock.ptp_clock);

 ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action,
           ixp_clock.ptp_clock);
 if (ret) {
  dev_err(dev, "failed to install clock removal handler\n");
  return ret;
 }

 __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
 __raw_writel(1, &ixp_clock.regs->trgt_lo);
 __raw_writel(0, &ixp_clock.regs->trgt_hi);
 __raw_writel(TTIPEND, &ixp_clock.regs->event);

 ret = devm_request_irq(dev, ixp_clock.master_irq, isr,
          0, DRIVER, &ixp_clock);
 if (ret)
  return dev_err_probe(dev, ret,
         "request_irq failed for irq %d\n",
         ixp_clock.master_irq);

 ret = devm_request_irq(dev, ixp_clock.slave_irq, isr,
          0, DRIVER, &ixp_clock);
 if (ret)
  return dev_err_probe(dev, ret,
         "request_irq failed for irq %d\n",
         ixp_clock.slave_irq);

 return 0;
}

static const struct of_device_id ptp_ixp_match[] = {
 {
  .compatible = "intel,ixp46x-ptp-timer",
 },
 { },
};

static struct platform_driver ptp_ixp_driver = {
 .driver = {
  .name = "ptp-ixp46x",
  .of_match_table = ptp_ixp_match,
  .suppress_bind_attrs = true,
 },
 .probe = ptp_ixp_probe,
};
module_platform_driver(ptp_ixp_driver);

MODULE_AUTHOR("Richard Cochran ");
MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
MODULE_LICENSE("GPL");

Messung V0.5
C=98 H=100 G=98

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.