162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DFL device driver for Time-of-Day (ToD) private feature 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 Intel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dfl.h> 1162306a36Sopenharmony_ci#include <linux/gcd.h> 1262306a36Sopenharmony_ci#include <linux/iopoll.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1562306a36Sopenharmony_ci#include <linux/spinlock.h> 1662306a36Sopenharmony_ci#include <linux/units.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define FME_FEATURE_ID_TOD 0x22 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* ToD clock register space. */ 2162306a36Sopenharmony_ci#define TOD_CLK_FREQ 0x038 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * The read sequence of ToD timestamp registers: TOD_NANOSEC, TOD_SECONDSL and 2562306a36Sopenharmony_ci * TOD_SECONDSH, because there is a hardware snapshot whenever the TOD_NANOSEC 2662306a36Sopenharmony_ci * register is read. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The ToD IP requires writing registers in the reverse order to the read sequence. 2962306a36Sopenharmony_ci * The timestamp is corrected when the TOD_NANOSEC register is written, so the 3062306a36Sopenharmony_ci * sequence of write TOD registers: TOD_SECONDSH, TOD_SECONDSL and TOD_NANOSEC. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define TOD_SECONDSH 0x100 3362306a36Sopenharmony_ci#define TOD_SECONDSL 0x104 3462306a36Sopenharmony_ci#define TOD_NANOSEC 0x108 3562306a36Sopenharmony_ci#define TOD_PERIOD 0x110 3662306a36Sopenharmony_ci#define TOD_ADJUST_PERIOD 0x114 3762306a36Sopenharmony_ci#define TOD_ADJUST_COUNT 0x118 3862306a36Sopenharmony_ci#define TOD_DRIFT_ADJUST 0x11c 3962306a36Sopenharmony_ci#define TOD_DRIFT_ADJUST_RATE 0x120 4062306a36Sopenharmony_ci#define PERIOD_FRAC_OFFSET 16 4162306a36Sopenharmony_ci#define SECONDS_MSB GENMASK_ULL(47, 32) 4262306a36Sopenharmony_ci#define SECONDS_LSB GENMASK_ULL(31, 0) 4362306a36Sopenharmony_ci#define TOD_SECONDSH_SEC_MSB GENMASK_ULL(15, 0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define CAL_SECONDS(m, l) ((FIELD_GET(TOD_SECONDSH_SEC_MSB, (m)) << 32) | (l)) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define TOD_PERIOD_MASK GENMASK_ULL(19, 0) 4862306a36Sopenharmony_ci#define TOD_PERIOD_MAX FIELD_MAX(TOD_PERIOD_MASK) 4962306a36Sopenharmony_ci#define TOD_PERIOD_MIN 0 5062306a36Sopenharmony_ci#define TOD_DRIFT_ADJUST_MASK GENMASK_ULL(15, 0) 5162306a36Sopenharmony_ci#define TOD_DRIFT_ADJUST_FNS_MAX FIELD_MAX(TOD_DRIFT_ADJUST_MASK) 5262306a36Sopenharmony_ci#define TOD_DRIFT_ADJUST_RATE_MAX TOD_DRIFT_ADJUST_FNS_MAX 5362306a36Sopenharmony_ci#define TOD_ADJUST_COUNT_MASK GENMASK_ULL(19, 0) 5462306a36Sopenharmony_ci#define TOD_ADJUST_COUNT_MAX FIELD_MAX(TOD_ADJUST_COUNT_MASK) 5562306a36Sopenharmony_ci#define TOD_ADJUST_INTERVAL_US 10 5662306a36Sopenharmony_ci#define TOD_ADJUST_MS \ 5762306a36Sopenharmony_ci (((TOD_PERIOD_MAX >> 16) + 1) * (TOD_ADJUST_COUNT_MAX + 1)) 5862306a36Sopenharmony_ci#define TOD_ADJUST_MS_MAX (TOD_ADJUST_MS / MICRO) 5962306a36Sopenharmony_ci#define TOD_ADJUST_MAX_US (TOD_ADJUST_MS_MAX * USEC_PER_MSEC) 6062306a36Sopenharmony_ci#define TOD_MAX_ADJ (500 * MEGA) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct dfl_tod { 6362306a36Sopenharmony_ci struct ptp_clock_info ptp_clock_ops; 6462306a36Sopenharmony_ci struct device *dev; 6562306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* ToD Clock address space */ 6862306a36Sopenharmony_ci void __iomem *tod_ctrl; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* ToD clock registers protection */ 7162306a36Sopenharmony_ci spinlock_t tod_lock; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * A fine ToD HW clock offset adjustment. To perform the fine offset adjustment, the 7662306a36Sopenharmony_ci * adjust_period and adjust_count argument are used to update the TOD_ADJUST_PERIOD 7762306a36Sopenharmony_ci * and TOD_ADJUST_COUNT register for in hardware. The dt->tod_lock spinlock must be 7862306a36Sopenharmony_ci * held when calling this function. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic int fine_adjust_tod_clock(struct dfl_tod *dt, u32 adjust_period, 8162306a36Sopenharmony_ci u32 adjust_count) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 8462306a36Sopenharmony_ci u32 val; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci writel(adjust_period, base + TOD_ADJUST_PERIOD); 8762306a36Sopenharmony_ci writel(adjust_count, base + TOD_ADJUST_COUNT); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Wait for present offset adjustment update to complete */ 9062306a36Sopenharmony_ci return readl_poll_timeout_atomic(base + TOD_ADJUST_COUNT, val, !val, TOD_ADJUST_INTERVAL_US, 9162306a36Sopenharmony_ci TOD_ADJUST_MAX_US); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * A coarse ToD HW clock offset adjustment. The coarse time adjustment performs by 9662306a36Sopenharmony_ci * adding or subtracting the delta value from the current ToD HW clock time. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic int coarse_adjust_tod_clock(struct dfl_tod *dt, s64 delta) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u32 seconds_msb, seconds_lsb, nanosec; 10162306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 10262306a36Sopenharmony_ci u64 seconds, now; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (delta == 0) 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci nanosec = readl(base + TOD_NANOSEC); 10862306a36Sopenharmony_ci seconds_lsb = readl(base + TOD_SECONDSL); 10962306a36Sopenharmony_ci seconds_msb = readl(base + TOD_SECONDSH); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Calculate new time */ 11262306a36Sopenharmony_ci seconds = CAL_SECONDS(seconds_msb, seconds_lsb); 11362306a36Sopenharmony_ci now = seconds * NSEC_PER_SEC + nanosec + delta; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci seconds = div_u64_rem(now, NSEC_PER_SEC, &nanosec); 11662306a36Sopenharmony_ci seconds_msb = FIELD_GET(SECONDS_MSB, seconds); 11762306a36Sopenharmony_ci seconds_lsb = FIELD_GET(SECONDS_LSB, seconds); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci writel(seconds_msb, base + TOD_SECONDSH); 12062306a36Sopenharmony_ci writel(seconds_lsb, base + TOD_SECONDSL); 12162306a36Sopenharmony_ci writel(nanosec, base + TOD_NANOSEC); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int dfl_tod_adjust_fine(struct ptp_clock_info *ptp, long scaled_ppm) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops); 12962306a36Sopenharmony_ci u32 tod_period, tod_rem, tod_drift_adjust_fns, tod_drift_adjust_rate; 13062306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 13162306a36Sopenharmony_ci unsigned long flags, rate; 13262306a36Sopenharmony_ci u64 ppb; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Get the clock rate from clock frequency register offset */ 13562306a36Sopenharmony_ci rate = readl(base + TOD_CLK_FREQ); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* add GIGA as nominal ppb */ 13862306a36Sopenharmony_ci ppb = scaled_ppm_to_ppb(scaled_ppm) + GIGA; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci tod_period = div_u64_rem(ppb << PERIOD_FRAC_OFFSET, rate, &tod_rem); 14162306a36Sopenharmony_ci if (tod_period > TOD_PERIOD_MAX) 14262306a36Sopenharmony_ci return -ERANGE; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * The drift of ToD adjusted periodically by adding a drift_adjust_fns 14662306a36Sopenharmony_ci * correction value every drift_adjust_rate count of clock cycles. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci tod_drift_adjust_fns = tod_rem / gcd(tod_rem, rate); 14962306a36Sopenharmony_ci tod_drift_adjust_rate = rate / gcd(tod_rem, rate); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci while ((tod_drift_adjust_fns > TOD_DRIFT_ADJUST_FNS_MAX) || 15262306a36Sopenharmony_ci (tod_drift_adjust_rate > TOD_DRIFT_ADJUST_RATE_MAX)) { 15362306a36Sopenharmony_ci tod_drift_adjust_fns >>= 1; 15462306a36Sopenharmony_ci tod_drift_adjust_rate >>= 1; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (tod_drift_adjust_fns == 0) 15862306a36Sopenharmony_ci tod_drift_adjust_rate = 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci spin_lock_irqsave(&dt->tod_lock, flags); 16162306a36Sopenharmony_ci writel(tod_period, base + TOD_PERIOD); 16262306a36Sopenharmony_ci writel(0, base + TOD_ADJUST_PERIOD); 16362306a36Sopenharmony_ci writel(0, base + TOD_ADJUST_COUNT); 16462306a36Sopenharmony_ci writel(tod_drift_adjust_fns, base + TOD_DRIFT_ADJUST); 16562306a36Sopenharmony_ci writel(tod_drift_adjust_rate, base + TOD_DRIFT_ADJUST_RATE); 16662306a36Sopenharmony_ci spin_unlock_irqrestore(&dt->tod_lock, flags); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int dfl_tod_adjust_time(struct ptp_clock_info *ptp, s64 delta) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops); 17462306a36Sopenharmony_ci u32 period, diff, rem, rem_period, adj_period; 17562306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 17662306a36Sopenharmony_ci unsigned long flags; 17762306a36Sopenharmony_ci bool neg_adj; 17862306a36Sopenharmony_ci u64 count; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci neg_adj = delta < 0; 18262306a36Sopenharmony_ci if (neg_adj) 18362306a36Sopenharmony_ci delta = -delta; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci spin_lock_irqsave(&dt->tod_lock, flags); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * Get the maximum possible value of the Period register offset 18962306a36Sopenharmony_ci * adjustment in nanoseconds scale. This depends on the current 19062306a36Sopenharmony_ci * Period register setting and the maximum and minimum possible 19162306a36Sopenharmony_ci * values of the Period register. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci period = readl(base + TOD_PERIOD); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (neg_adj) { 19662306a36Sopenharmony_ci diff = (period - TOD_PERIOD_MIN) >> PERIOD_FRAC_OFFSET; 19762306a36Sopenharmony_ci adj_period = period - (diff << PERIOD_FRAC_OFFSET); 19862306a36Sopenharmony_ci count = div_u64_rem(delta, diff, &rem); 19962306a36Sopenharmony_ci rem_period = period - (rem << PERIOD_FRAC_OFFSET); 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci diff = (TOD_PERIOD_MAX - period) >> PERIOD_FRAC_OFFSET; 20262306a36Sopenharmony_ci adj_period = period + (diff << PERIOD_FRAC_OFFSET); 20362306a36Sopenharmony_ci count = div_u64_rem(delta, diff, &rem); 20462306a36Sopenharmony_ci rem_period = period + (rem << PERIOD_FRAC_OFFSET); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ret = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (count > TOD_ADJUST_COUNT_MAX) { 21062306a36Sopenharmony_ci ret = coarse_adjust_tod_clock(dt, delta); 21162306a36Sopenharmony_ci } else { 21262306a36Sopenharmony_ci /* Adjust the period by count cycles to adjust the time */ 21362306a36Sopenharmony_ci if (count) 21462306a36Sopenharmony_ci ret = fine_adjust_tod_clock(dt, adj_period, count); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* If there is a remainder, adjust the period for an additional cycle */ 21762306a36Sopenharmony_ci if (rem) 21862306a36Sopenharmony_ci ret = fine_adjust_tod_clock(dt, rem_period, 1); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci spin_unlock_irqrestore(&dt->tod_lock, flags); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int dfl_tod_get_timex(struct ptp_clock_info *ptp, struct timespec64 *ts, 22762306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops); 23062306a36Sopenharmony_ci u32 seconds_msb, seconds_lsb, nanosec; 23162306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 23262306a36Sopenharmony_ci unsigned long flags; 23362306a36Sopenharmony_ci u64 seconds; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci spin_lock_irqsave(&dt->tod_lock, flags); 23662306a36Sopenharmony_ci ptp_read_system_prets(sts); 23762306a36Sopenharmony_ci nanosec = readl(base + TOD_NANOSEC); 23862306a36Sopenharmony_ci seconds_lsb = readl(base + TOD_SECONDSL); 23962306a36Sopenharmony_ci seconds_msb = readl(base + TOD_SECONDSH); 24062306a36Sopenharmony_ci ptp_read_system_postts(sts); 24162306a36Sopenharmony_ci spin_unlock_irqrestore(&dt->tod_lock, flags); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci seconds = CAL_SECONDS(seconds_msb, seconds_lsb); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ts->tv_nsec = nanosec; 24662306a36Sopenharmony_ci ts->tv_sec = seconds; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int dfl_tod_set_time(struct ptp_clock_info *ptp, 25262306a36Sopenharmony_ci const struct timespec64 *ts) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops); 25562306a36Sopenharmony_ci u32 seconds_msb = FIELD_GET(SECONDS_MSB, ts->tv_sec); 25662306a36Sopenharmony_ci u32 seconds_lsb = FIELD_GET(SECONDS_LSB, ts->tv_sec); 25762306a36Sopenharmony_ci u32 nanosec = FIELD_GET(SECONDS_LSB, ts->tv_nsec); 25862306a36Sopenharmony_ci void __iomem *base = dt->tod_ctrl; 25962306a36Sopenharmony_ci unsigned long flags; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci spin_lock_irqsave(&dt->tod_lock, flags); 26262306a36Sopenharmony_ci writel(seconds_msb, base + TOD_SECONDSH); 26362306a36Sopenharmony_ci writel(seconds_lsb, base + TOD_SECONDSL); 26462306a36Sopenharmony_ci writel(nanosec, base + TOD_NANOSEC); 26562306a36Sopenharmony_ci spin_unlock_irqrestore(&dt->tod_lock, flags); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic struct ptp_clock_info dfl_tod_clock_ops = { 27162306a36Sopenharmony_ci .owner = THIS_MODULE, 27262306a36Sopenharmony_ci .name = "dfl_tod", 27362306a36Sopenharmony_ci .max_adj = TOD_MAX_ADJ, 27462306a36Sopenharmony_ci .adjfine = dfl_tod_adjust_fine, 27562306a36Sopenharmony_ci .adjtime = dfl_tod_adjust_time, 27662306a36Sopenharmony_ci .gettimex64 = dfl_tod_get_timex, 27762306a36Sopenharmony_ci .settime64 = dfl_tod_set_time, 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int dfl_tod_probe(struct dfl_device *ddev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct device *dev = &ddev->dev; 28362306a36Sopenharmony_ci struct dfl_tod *dt; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL); 28662306a36Sopenharmony_ci if (!dt) 28762306a36Sopenharmony_ci return -ENOMEM; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dt->tod_ctrl = devm_ioremap_resource(dev, &ddev->mmio_res); 29062306a36Sopenharmony_ci if (IS_ERR(dt->tod_ctrl)) 29162306a36Sopenharmony_ci return PTR_ERR(dt->tod_ctrl); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci dt->dev = dev; 29462306a36Sopenharmony_ci spin_lock_init(&dt->tod_lock); 29562306a36Sopenharmony_ci dev_set_drvdata(dev, dt); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci dt->ptp_clock_ops = dfl_tod_clock_ops; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci dt->ptp_clock = ptp_clock_register(&dt->ptp_clock_ops, dev); 30062306a36Sopenharmony_ci if (IS_ERR(dt->ptp_clock)) 30162306a36Sopenharmony_ci return dev_err_probe(dt->dev, PTR_ERR(dt->ptp_clock), 30262306a36Sopenharmony_ci "Unable to register PTP clock\n"); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void dfl_tod_remove(struct dfl_device *ddev) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct dfl_tod *dt = dev_get_drvdata(&ddev->dev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ptp_clock_unregister(dt->ptp_clock); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic const struct dfl_device_id dfl_tod_ids[] = { 31562306a36Sopenharmony_ci { FME_ID, FME_FEATURE_ID_TOD }, 31662306a36Sopenharmony_ci { } 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(dfl, dfl_tod_ids); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic struct dfl_driver dfl_tod_driver = { 32162306a36Sopenharmony_ci .drv = { 32262306a36Sopenharmony_ci .name = "dfl-tod", 32362306a36Sopenharmony_ci }, 32462306a36Sopenharmony_ci .id_table = dfl_tod_ids, 32562306a36Sopenharmony_ci .probe = dfl_tod_probe, 32662306a36Sopenharmony_ci .remove = dfl_tod_remove, 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_cimodule_dfl_driver(dfl_tod_driver); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciMODULE_DESCRIPTION("FPGA DFL ToD driver"); 33162306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 33262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 333