162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_platform.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/timekeeping.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/of_irq.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "icss_iep.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define IEP_MAX_DEF_INC 0xf 2462306a36Sopenharmony_ci#define IEP_MAX_COMPEN_INC 0xfff 2562306a36Sopenharmony_ci#define IEP_MAX_COMPEN_COUNT 0xffffff 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define IEP_GLOBAL_CFG_CNT_ENABLE BIT(0) 2862306a36Sopenharmony_ci#define IEP_GLOBAL_CFG_DEFAULT_INC_MASK GENMASK(7, 4) 2962306a36Sopenharmony_ci#define IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT 4 3062306a36Sopenharmony_ci#define IEP_GLOBAL_CFG_COMPEN_INC_MASK GENMASK(19, 8) 3162306a36Sopenharmony_ci#define IEP_GLOBAL_CFG_COMPEN_INC_SHIFT 8 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define IEP_GLOBAL_STATUS_CNT_OVF BIT(0) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define IEP_CMP_CFG_SHADOW_EN BIT(17) 3662306a36Sopenharmony_ci#define IEP_CMP_CFG_CMP0_RST_CNT_EN BIT(0) 3762306a36Sopenharmony_ci#define IEP_CMP_CFG_CMP_EN(cmp) (GENMASK(16, 1) & (1 << ((cmp) + 1))) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define IEP_CMP_STATUS(cmp) (1 << (cmp)) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define IEP_SYNC_CTRL_SYNC_EN BIT(0) 4262306a36Sopenharmony_ci#define IEP_SYNC_CTRL_SYNC_N_EN(n) (GENMASK(2, 1) & (BIT(1) << (n))) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define IEP_MIN_CMP 0 4562306a36Sopenharmony_ci#define IEP_MAX_CMP 15 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define ICSS_IEP_64BIT_COUNTER_SUPPORT BIT(0) 4862306a36Sopenharmony_ci#define ICSS_IEP_SLOW_COMPEN_REG_SUPPORT BIT(1) 4962306a36Sopenharmony_ci#define ICSS_IEP_SHADOW_MODE_SUPPORT BIT(2) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define LATCH_INDEX(ts_index) ((ts_index) + 6) 5262306a36Sopenharmony_ci#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n)) 5362306a36Sopenharmony_ci#define IEP_CAP_CFG_CAP_ASYNC_EN(n) BIT(LATCH_INDEX(n) + 10) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cienum { 5662306a36Sopenharmony_ci ICSS_IEP_GLOBAL_CFG_REG, 5762306a36Sopenharmony_ci ICSS_IEP_GLOBAL_STATUS_REG, 5862306a36Sopenharmony_ci ICSS_IEP_COMPEN_REG, 5962306a36Sopenharmony_ci ICSS_IEP_SLOW_COMPEN_REG, 6062306a36Sopenharmony_ci ICSS_IEP_COUNT_REG0, 6162306a36Sopenharmony_ci ICSS_IEP_COUNT_REG1, 6262306a36Sopenharmony_ci ICSS_IEP_CAPTURE_CFG_REG, 6362306a36Sopenharmony_ci ICSS_IEP_CAPTURE_STAT_REG, 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ICSS_IEP_CAP6_RISE_REG0, 6662306a36Sopenharmony_ci ICSS_IEP_CAP6_RISE_REG1, 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ICSS_IEP_CAP7_RISE_REG0, 6962306a36Sopenharmony_ci ICSS_IEP_CAP7_RISE_REG1, 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ICSS_IEP_CMP_CFG_REG, 7262306a36Sopenharmony_ci ICSS_IEP_CMP_STAT_REG, 7362306a36Sopenharmony_ci ICSS_IEP_CMP0_REG0, 7462306a36Sopenharmony_ci ICSS_IEP_CMP0_REG1, 7562306a36Sopenharmony_ci ICSS_IEP_CMP1_REG0, 7662306a36Sopenharmony_ci ICSS_IEP_CMP1_REG1, 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ICSS_IEP_CMP8_REG0, 7962306a36Sopenharmony_ci ICSS_IEP_CMP8_REG1, 8062306a36Sopenharmony_ci ICSS_IEP_SYNC_CTRL_REG, 8162306a36Sopenharmony_ci ICSS_IEP_SYNC0_STAT_REG, 8262306a36Sopenharmony_ci ICSS_IEP_SYNC1_STAT_REG, 8362306a36Sopenharmony_ci ICSS_IEP_SYNC_PWIDTH_REG, 8462306a36Sopenharmony_ci ICSS_IEP_SYNC0_PERIOD_REG, 8562306a36Sopenharmony_ci ICSS_IEP_SYNC1_DELAY_REG, 8662306a36Sopenharmony_ci ICSS_IEP_SYNC_START_REG, 8762306a36Sopenharmony_ci ICSS_IEP_MAX_REGS, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * struct icss_iep_plat_data - Plat data to handle SoC variants 9262306a36Sopenharmony_ci * @config: Regmap configuration data 9362306a36Sopenharmony_ci * @reg_offs: register offsets to capture offset differences across SoCs 9462306a36Sopenharmony_ci * @flags: Flags to represent IEP properties 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistruct icss_iep_plat_data { 9762306a36Sopenharmony_ci struct regmap_config *config; 9862306a36Sopenharmony_ci u32 reg_offs[ICSS_IEP_MAX_REGS]; 9962306a36Sopenharmony_ci u32 flags; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct icss_iep { 10362306a36Sopenharmony_ci struct device *dev; 10462306a36Sopenharmony_ci void __iomem *base; 10562306a36Sopenharmony_ci const struct icss_iep_plat_data *plat_data; 10662306a36Sopenharmony_ci struct regmap *map; 10762306a36Sopenharmony_ci struct device_node *client_np; 10862306a36Sopenharmony_ci unsigned long refclk_freq; 10962306a36Sopenharmony_ci int clk_tick_time; /* one refclk tick time in ns */ 11062306a36Sopenharmony_ci struct ptp_clock_info ptp_info; 11162306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 11262306a36Sopenharmony_ci struct mutex ptp_clk_mutex; /* PHC access serializer */ 11362306a36Sopenharmony_ci spinlock_t irq_lock; /* CMP IRQ vs icss_iep_ptp_enable access */ 11462306a36Sopenharmony_ci u32 def_inc; 11562306a36Sopenharmony_ci s16 slow_cmp_inc; 11662306a36Sopenharmony_ci u32 slow_cmp_count; 11762306a36Sopenharmony_ci const struct icss_iep_clockops *ops; 11862306a36Sopenharmony_ci void *clockops_data; 11962306a36Sopenharmony_ci u32 cycle_time_ns; 12062306a36Sopenharmony_ci u32 perout_enabled; 12162306a36Sopenharmony_ci bool pps_enabled; 12262306a36Sopenharmony_ci int cap_cmp_irq; 12362306a36Sopenharmony_ci u64 period; 12462306a36Sopenharmony_ci u32 latch_enable; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * icss_iep_get_count_hi() - Get the upper 32 bit IEP counter 12962306a36Sopenharmony_ci * @iep: Pointer to structure representing IEP. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Return: upper 32 bit IEP counter 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ciint icss_iep_get_count_hi(struct icss_iep *iep) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u32 val = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (iep && (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)) 13862306a36Sopenharmony_ci val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return val; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_get_count_hi); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/** 14562306a36Sopenharmony_ci * icss_iep_get_count_low() - Get the lower 32 bit IEP counter 14662306a36Sopenharmony_ci * @iep: Pointer to structure representing IEP. 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Return: lower 32 bit IEP counter 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ciint icss_iep_get_count_low(struct icss_iep *iep) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 val = 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (iep) 15562306a36Sopenharmony_ci val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return val; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_get_count_low); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver 16362306a36Sopenharmony_ci * @iep: Pointer to structure representing IEP. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Return: PTP clock index, -1 if not registered 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ciint icss_iep_get_ptp_clock_idx(struct icss_iep *iep) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci if (!iep || !iep->ptp_clock) 17062306a36Sopenharmony_ci return -1; 17162306a36Sopenharmony_ci return ptp_clock_index(iep->ptp_clock); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_get_ptp_clock_idx); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void icss_iep_set_counter(struct icss_iep *iep, u64 ns) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 17862306a36Sopenharmony_ci writel(upper_32_bits(ns), iep->base + 17962306a36Sopenharmony_ci iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]); 18062306a36Sopenharmony_ci writel(lower_32_bits(ns), iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/** 18662306a36Sopenharmony_ci * icss_iep_settime() - Set time of the PTP clock using IEP driver 18762306a36Sopenharmony_ci * @iep: Pointer to structure representing IEP. 18862306a36Sopenharmony_ci * @ns: Time to be set in nanoseconds 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * This API uses writel() instead of regmap_write() for write operations as 19162306a36Sopenharmony_ci * regmap_write() is too slow and this API is time sensitive. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic void icss_iep_settime(struct icss_iep *iep, u64 ns) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci unsigned long flags; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (iep->ops && iep->ops->settime) { 19862306a36Sopenharmony_ci iep->ops->settime(iep->clockops_data, ns); 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci spin_lock_irqsave(&iep->irq_lock, flags); 20362306a36Sopenharmony_ci if (iep->pps_enabled || iep->perout_enabled) 20462306a36Sopenharmony_ci writel(0, iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci icss_iep_set_counter(iep, ns); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (iep->pps_enabled || iep->perout_enabled) { 20962306a36Sopenharmony_ci icss_iep_update_to_next_boundary(iep, ns); 21062306a36Sopenharmony_ci writel(IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN, 21162306a36Sopenharmony_ci iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci spin_unlock_irqrestore(&iep->irq_lock, flags); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/** 21762306a36Sopenharmony_ci * icss_iep_gettime() - Get time of the PTP clock using IEP driver 21862306a36Sopenharmony_ci * @iep: Pointer to structure representing IEP. 21962306a36Sopenharmony_ci * @sts: Pointer to structure representing PTP system timestamp. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * This API uses readl() instead of regmap_read() for read operations as 22262306a36Sopenharmony_ci * regmap_read() is too slow and this API is time sensitive. 22362306a36Sopenharmony_ci * 22462306a36Sopenharmony_ci * Return: The current timestamp of the PTP clock using IEP driver 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistatic u64 icss_iep_gettime(struct icss_iep *iep, 22762306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci u32 ts_hi = 0, ts_lo; 23062306a36Sopenharmony_ci unsigned long flags; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (iep->ops && iep->ops->gettime) 23362306a36Sopenharmony_ci return iep->ops->gettime(iep->clockops_data, sts); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* use local_irq_x() to make it work for both RT/non-RT */ 23662306a36Sopenharmony_ci local_irq_save(flags); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* no need to play with hi-lo, hi is latched when lo is read */ 23962306a36Sopenharmony_ci ptp_read_system_prets(sts); 24062306a36Sopenharmony_ci ts_lo = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]); 24162306a36Sopenharmony_ci ptp_read_system_postts(sts); 24262306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 24362306a36Sopenharmony_ci ts_hi = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci local_irq_restore(flags); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return (u64)ts_lo | (u64)ts_hi << 32; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void icss_iep_enable(struct icss_iep *iep) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, 25362306a36Sopenharmony_ci IEP_GLOBAL_CFG_CNT_ENABLE, 25462306a36Sopenharmony_ci IEP_GLOBAL_CFG_CNT_ENABLE); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void icss_iep_disable(struct icss_iep *iep) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, 26062306a36Sopenharmony_ci IEP_GLOBAL_CFG_CNT_ENABLE, 26162306a36Sopenharmony_ci 0); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void icss_iep_enable_shadow_mode(struct icss_iep *iep) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci u32 cycle_time; 26762306a36Sopenharmony_ci int cmp; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cycle_time = iep->cycle_time_ns - iep->def_inc; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci icss_iep_disable(iep); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* disable shadow mode */ 27462306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 27562306a36Sopenharmony_ci IEP_CMP_CFG_SHADOW_EN, 0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* enable shadow mode */ 27862306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 27962306a36Sopenharmony_ci IEP_CMP_CFG_SHADOW_EN, IEP_CMP_CFG_SHADOW_EN); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* clear counters */ 28262306a36Sopenharmony_ci icss_iep_set_counter(iep, 0); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* clear overflow status */ 28562306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_STATUS_REG, 28662306a36Sopenharmony_ci IEP_GLOBAL_STATUS_CNT_OVF, 28762306a36Sopenharmony_ci IEP_GLOBAL_STATUS_CNT_OVF); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* clear compare status */ 29062306a36Sopenharmony_ci for (cmp = IEP_MIN_CMP; cmp < IEP_MAX_CMP; cmp++) { 29162306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_STAT_REG, 29262306a36Sopenharmony_ci IEP_CMP_STATUS(cmp), IEP_CMP_STATUS(cmp)); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* enable reset counter on CMP0 event */ 29662306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 29762306a36Sopenharmony_ci IEP_CMP_CFG_CMP0_RST_CNT_EN, 29862306a36Sopenharmony_ci IEP_CMP_CFG_CMP0_RST_CNT_EN); 29962306a36Sopenharmony_ci /* enable compare */ 30062306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 30162306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(0), 30262306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(0)); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* set CMP0 value to cycle time */ 30562306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP0_REG0, cycle_time); 30662306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 30762306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP0_REG1, cycle_time); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci icss_iep_set_counter(iep, 0); 31062306a36Sopenharmony_ci icss_iep_enable(iep); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void icss_iep_set_default_inc(struct icss_iep *iep, u8 def_inc) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, 31662306a36Sopenharmony_ci IEP_GLOBAL_CFG_DEFAULT_INC_MASK, 31762306a36Sopenharmony_ci def_inc << IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void icss_iep_set_compensation_inc(struct icss_iep *iep, u16 compen_inc) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct device *dev = regmap_get_device(iep->map); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (compen_inc > IEP_MAX_COMPEN_INC) { 32562306a36Sopenharmony_ci dev_err(dev, "%s: too high compensation inc %d\n", 32662306a36Sopenharmony_ci __func__, compen_inc); 32762306a36Sopenharmony_ci compen_inc = IEP_MAX_COMPEN_INC; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG, 33162306a36Sopenharmony_ci IEP_GLOBAL_CFG_COMPEN_INC_MASK, 33262306a36Sopenharmony_ci compen_inc << IEP_GLOBAL_CFG_COMPEN_INC_SHIFT); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void icss_iep_set_compensation_count(struct icss_iep *iep, 33662306a36Sopenharmony_ci u32 compen_count) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct device *dev = regmap_get_device(iep->map); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (compen_count > IEP_MAX_COMPEN_COUNT) { 34162306a36Sopenharmony_ci dev_err(dev, "%s: too high compensation count %d\n", 34262306a36Sopenharmony_ci __func__, compen_count); 34362306a36Sopenharmony_ci compen_count = IEP_MAX_COMPEN_COUNT; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_COMPEN_REG, compen_count); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void icss_iep_set_slow_compensation_count(struct icss_iep *iep, 35062306a36Sopenharmony_ci u32 compen_count) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* PTP PHC operations */ 35662306a36Sopenharmony_cistatic int icss_iep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); 35962306a36Sopenharmony_ci s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 36062306a36Sopenharmony_ci u32 cyc_count; 36162306a36Sopenharmony_ci u16 cmp_inc; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* ppb is amount of frequency we want to adjust in 1GHz (billion) 36662306a36Sopenharmony_ci * e.g. 100ppb means we need to speed up clock by 100Hz 36762306a36Sopenharmony_ci * i.e. at end of 1 second (1 billion ns) clock time, we should be 36862306a36Sopenharmony_ci * counting 100 more ns. 36962306a36Sopenharmony_ci * We use IEP slow compensation to achieve continuous freq. adjustment. 37062306a36Sopenharmony_ci * There are 2 parts. Cycle time and adjustment per cycle. 37162306a36Sopenharmony_ci * Simplest case would be 1 sec Cycle time. Then adjustment 37262306a36Sopenharmony_ci * pre cycle would be (def_inc + ppb) value. 37362306a36Sopenharmony_ci * Cycle time will have to be chosen based on how worse the ppb is. 37462306a36Sopenharmony_ci * e.g. smaller the ppb, cycle time has to be large. 37562306a36Sopenharmony_ci * The minimum adjustment we can do is +-1ns per cycle so let's 37662306a36Sopenharmony_ci * reduce the cycle time to get 1ns per cycle adjustment. 37762306a36Sopenharmony_ci * 1ppb = 1sec cycle time & 1ns adjust 37862306a36Sopenharmony_ci * 1000ppb = 1/1000 cycle time & 1ns adjust per cycle 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (iep->cycle_time_ns) 38262306a36Sopenharmony_ci iep->slow_cmp_inc = iep->clk_tick_time; /* 4ns adj per cycle */ 38362306a36Sopenharmony_ci else 38462306a36Sopenharmony_ci iep->slow_cmp_inc = 1; /* 1ns adjust per cycle */ 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (ppb < 0) { 38762306a36Sopenharmony_ci iep->slow_cmp_inc = -iep->slow_cmp_inc; 38862306a36Sopenharmony_ci ppb = -ppb; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci cyc_count = NSEC_PER_SEC; /* 1s cycle time @1GHz */ 39262306a36Sopenharmony_ci cyc_count /= ppb; /* cycle time per ppb */ 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* slow_cmp_count is decremented every clock cycle, e.g. @250MHz */ 39562306a36Sopenharmony_ci if (!iep->cycle_time_ns) 39662306a36Sopenharmony_ci cyc_count /= iep->clk_tick_time; 39762306a36Sopenharmony_ci iep->slow_cmp_count = cyc_count; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* iep->clk_tick_time is def_inc */ 40062306a36Sopenharmony_ci cmp_inc = iep->clk_tick_time + iep->slow_cmp_inc; 40162306a36Sopenharmony_ci icss_iep_set_compensation_inc(iep, cmp_inc); 40262306a36Sopenharmony_ci icss_iep_set_slow_compensation_count(iep, iep->slow_cmp_count); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int icss_iep_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); 41262306a36Sopenharmony_ci s64 ns; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 41562306a36Sopenharmony_ci if (iep->ops && iep->ops->adjtime) { 41662306a36Sopenharmony_ci iep->ops->adjtime(iep->clockops_data, delta); 41762306a36Sopenharmony_ci } else { 41862306a36Sopenharmony_ci ns = icss_iep_gettime(iep, NULL); 41962306a36Sopenharmony_ci ns += delta; 42062306a36Sopenharmony_ci icss_iep_settime(iep, ns); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int icss_iep_ptp_gettimeex(struct ptp_clock_info *ptp, 42862306a36Sopenharmony_ci struct timespec64 *ts, 42962306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); 43262306a36Sopenharmony_ci u64 ns; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 43562306a36Sopenharmony_ci ns = icss_iep_gettime(iep, sts); 43662306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 43762306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int icss_iep_ptp_settime(struct ptp_clock_info *ptp, 44362306a36Sopenharmony_ci const struct timespec64 *ts) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); 44662306a36Sopenharmony_ci u64 ns; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 44962306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 45062306a36Sopenharmony_ci icss_iep_settime(iep, ns); 45162306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci u64 ns, p_ns; 45962306a36Sopenharmony_ci u32 offset; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ns = icss_iep_gettime(iep, NULL); 46262306a36Sopenharmony_ci if (start_ns < ns) 46362306a36Sopenharmony_ci start_ns = ns; 46462306a36Sopenharmony_ci p_ns = iep->period; 46562306a36Sopenharmony_ci /* Round up to next period boundary */ 46662306a36Sopenharmony_ci start_ns += p_ns - 1; 46762306a36Sopenharmony_ci offset = do_div(start_ns, p_ns); 46862306a36Sopenharmony_ci start_ns = start_ns * p_ns; 46962306a36Sopenharmony_ci /* If it is too close to update, shift to next boundary */ 47062306a36Sopenharmony_ci if (p_ns - offset < 10) 47162306a36Sopenharmony_ci start_ns += p_ns; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(start_ns)); 47462306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 47562306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(start_ns)); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int icss_iep_perout_enable_hw(struct icss_iep *iep, 47962306a36Sopenharmony_ci struct ptp_perout_request *req, int on) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci u64 cmp; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (iep->ops && iep->ops->perout_enable) { 48562306a36Sopenharmony_ci ret = iep->ops->perout_enable(iep->clockops_data, req, on, &cmp); 48662306a36Sopenharmony_ci if (ret) 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (on) { 49062306a36Sopenharmony_ci /* Configure CMP */ 49162306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp)); 49262306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 49362306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp)); 49462306a36Sopenharmony_ci /* Configure SYNC, 1ms pulse width */ 49562306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, 1000000); 49662306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); 49762306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_START_REG, 0); 49862306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */ 49962306a36Sopenharmony_ci /* Enable CMP 1 */ 50062306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 50162306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1)); 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci /* Disable CMP 1 */ 50462306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 50562306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(1), 0); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* clear regs */ 50862306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0); 50962306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 51062306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci if (on) { 51462306a36Sopenharmony_ci u64 start_ns; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci iep->period = ((u64)req->period.sec * NSEC_PER_SEC) + 51762306a36Sopenharmony_ci req->period.nsec; 51862306a36Sopenharmony_ci start_ns = ((u64)req->period.sec * NSEC_PER_SEC) 51962306a36Sopenharmony_ci + req->period.nsec; 52062306a36Sopenharmony_ci icss_iep_update_to_next_boundary(iep, start_ns); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Enable Sync in single shot mode */ 52362306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 52462306a36Sopenharmony_ci IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN); 52562306a36Sopenharmony_ci /* Enable CMP 1 */ 52662306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 52762306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1)); 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci /* Disable CMP 1 */ 53062306a36Sopenharmony_ci regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG, 53162306a36Sopenharmony_ci IEP_CMP_CFG_CMP_EN(1), 0); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* clear CMP regs */ 53462306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0); 53562306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) 53662306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* Disable sync */ 53962306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int icss_iep_perout_enable(struct icss_iep *iep, 54762306a36Sopenharmony_ci struct ptp_perout_request *req, int on) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci unsigned long flags; 55062306a36Sopenharmony_ci int ret = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (iep->pps_enabled) { 55562306a36Sopenharmony_ci ret = -EBUSY; 55662306a36Sopenharmony_ci goto exit; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (iep->perout_enabled == !!on) 56062306a36Sopenharmony_ci goto exit; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci spin_lock_irqsave(&iep->irq_lock, flags); 56362306a36Sopenharmony_ci ret = icss_iep_perout_enable_hw(iep, req, on); 56462306a36Sopenharmony_ci if (!ret) 56562306a36Sopenharmony_ci iep->perout_enabled = !!on; 56662306a36Sopenharmony_ci spin_unlock_irqrestore(&iep->irq_lock, flags); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciexit: 56962306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int icss_iep_pps_enable(struct icss_iep *iep, int on) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct ptp_clock_request rq; 57762306a36Sopenharmony_ci struct timespec64 ts; 57862306a36Sopenharmony_ci unsigned long flags; 57962306a36Sopenharmony_ci int ret = 0; 58062306a36Sopenharmony_ci u64 ns; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (iep->perout_enabled) { 58562306a36Sopenharmony_ci ret = -EBUSY; 58662306a36Sopenharmony_ci goto exit; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (iep->pps_enabled == !!on) 59062306a36Sopenharmony_ci goto exit; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci spin_lock_irqsave(&iep->irq_lock, flags); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci rq.perout.index = 0; 59562306a36Sopenharmony_ci if (on) { 59662306a36Sopenharmony_ci ns = icss_iep_gettime(iep, NULL); 59762306a36Sopenharmony_ci ts = ns_to_timespec64(ns); 59862306a36Sopenharmony_ci rq.perout.period.sec = 1; 59962306a36Sopenharmony_ci rq.perout.period.nsec = 0; 60062306a36Sopenharmony_ci rq.perout.start.sec = ts.tv_sec + 2; 60162306a36Sopenharmony_ci rq.perout.start.nsec = 0; 60262306a36Sopenharmony_ci ret = icss_iep_perout_enable_hw(iep, &rq.perout, on); 60362306a36Sopenharmony_ci } else { 60462306a36Sopenharmony_ci ret = icss_iep_perout_enable_hw(iep, &rq.perout, on); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!ret) 60862306a36Sopenharmony_ci iep->pps_enabled = !!on; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci spin_unlock_irqrestore(&iep->irq_lock, flags); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ciexit: 61362306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return ret; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int icss_iep_extts_enable(struct icss_iep *iep, u32 index, int on) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci u32 val, cap, ret = 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mutex_lock(&iep->ptp_clk_mutex); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (iep->ops && iep->ops->extts_enable) { 62562306a36Sopenharmony_ci ret = iep->ops->extts_enable(iep->clockops_data, index, on); 62662306a36Sopenharmony_ci goto exit; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (((iep->latch_enable & BIT(index)) >> index) == on) 63062306a36Sopenharmony_ci goto exit; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci regmap_read(iep->map, ICSS_IEP_CAPTURE_CFG_REG, &val); 63362306a36Sopenharmony_ci cap = IEP_CAP_CFG_CAP_ASYNC_EN(index) | IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(index); 63462306a36Sopenharmony_ci if (on) { 63562306a36Sopenharmony_ci val |= cap; 63662306a36Sopenharmony_ci iep->latch_enable |= BIT(index); 63762306a36Sopenharmony_ci } else { 63862306a36Sopenharmony_ci val &= ~cap; 63962306a36Sopenharmony_ci iep->latch_enable &= ~BIT(index); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_CAPTURE_CFG_REG, val); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ciexit: 64462306a36Sopenharmony_ci mutex_unlock(&iep->ptp_clk_mutex); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return ret; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic int icss_iep_ptp_enable(struct ptp_clock_info *ptp, 65062306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci switch (rq->type) { 65562306a36Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 65662306a36Sopenharmony_ci return icss_iep_perout_enable(iep, &rq->perout, on); 65762306a36Sopenharmony_ci case PTP_CLK_REQ_PPS: 65862306a36Sopenharmony_ci return icss_iep_pps_enable(iep, on); 65962306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 66062306a36Sopenharmony_ci return icss_iep_extts_enable(iep, rq->extts.index, on); 66162306a36Sopenharmony_ci default: 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return -EOPNOTSUPP; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic struct ptp_clock_info icss_iep_ptp_info = { 66962306a36Sopenharmony_ci .owner = THIS_MODULE, 67062306a36Sopenharmony_ci .name = "ICSS IEP timer", 67162306a36Sopenharmony_ci .max_adj = 10000000, 67262306a36Sopenharmony_ci .adjfine = icss_iep_ptp_adjfine, 67362306a36Sopenharmony_ci .adjtime = icss_iep_ptp_adjtime, 67462306a36Sopenharmony_ci .gettimex64 = icss_iep_ptp_gettimeex, 67562306a36Sopenharmony_ci .settime64 = icss_iep_ptp_settime, 67662306a36Sopenharmony_ci .enable = icss_iep_ptp_enable, 67762306a36Sopenharmony_ci}; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistruct icss_iep *icss_iep_get_idx(struct device_node *np, int idx) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct platform_device *pdev; 68262306a36Sopenharmony_ci struct device_node *iep_np; 68362306a36Sopenharmony_ci struct icss_iep *iep; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci iep_np = of_parse_phandle(np, "ti,iep", idx); 68662306a36Sopenharmony_ci if (!iep_np || !of_device_is_available(iep_np)) 68762306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci pdev = of_find_device_by_node(iep_np); 69062306a36Sopenharmony_ci of_node_put(iep_np); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!pdev) 69362306a36Sopenharmony_ci /* probably IEP not yet probed */ 69462306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci iep = platform_get_drvdata(pdev); 69762306a36Sopenharmony_ci if (!iep) 69862306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci device_lock(iep->dev); 70162306a36Sopenharmony_ci if (iep->client_np) { 70262306a36Sopenharmony_ci device_unlock(iep->dev); 70362306a36Sopenharmony_ci dev_err(iep->dev, "IEP is already acquired by %s", 70462306a36Sopenharmony_ci iep->client_np->name); 70562306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci iep->client_np = np; 70862306a36Sopenharmony_ci device_unlock(iep->dev); 70962306a36Sopenharmony_ci get_device(iep->dev); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return iep; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_get_idx); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistruct icss_iep *icss_iep_get(struct device_node *np) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci return icss_iep_get_idx(np, 0); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_get); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_civoid icss_iep_put(struct icss_iep *iep) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci device_lock(iep->dev); 72462306a36Sopenharmony_ci iep->client_np = NULL; 72562306a36Sopenharmony_ci device_unlock(iep->dev); 72662306a36Sopenharmony_ci put_device(iep->dev); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_put); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_civoid icss_iep_init_fw(struct icss_iep *iep) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci /* start IEP for FW use in raw 64bit mode, no PTP support */ 73362306a36Sopenharmony_ci iep->clk_tick_time = iep->def_inc; 73462306a36Sopenharmony_ci iep->cycle_time_ns = 0; 73562306a36Sopenharmony_ci iep->ops = NULL; 73662306a36Sopenharmony_ci iep->clockops_data = NULL; 73762306a36Sopenharmony_ci icss_iep_set_default_inc(iep, iep->def_inc); 73862306a36Sopenharmony_ci icss_iep_set_compensation_inc(iep, iep->def_inc); 73962306a36Sopenharmony_ci icss_iep_set_compensation_count(iep, 0); 74062306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */ 74162306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); 74262306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT) 74362306a36Sopenharmony_ci icss_iep_set_slow_compensation_count(iep, 0); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci icss_iep_enable(iep); 74662306a36Sopenharmony_ci icss_iep_settime(iep, 0); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_init_fw); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_civoid icss_iep_exit_fw(struct icss_iep *iep) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci icss_iep_disable(iep); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_exit_fw); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ciint icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops, 75762306a36Sopenharmony_ci void *clockops_data, u32 cycle_time_ns) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci int ret = 0; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci iep->cycle_time_ns = cycle_time_ns; 76262306a36Sopenharmony_ci iep->clk_tick_time = iep->def_inc; 76362306a36Sopenharmony_ci iep->ops = clkops; 76462306a36Sopenharmony_ci iep->clockops_data = clockops_data; 76562306a36Sopenharmony_ci icss_iep_set_default_inc(iep, iep->def_inc); 76662306a36Sopenharmony_ci icss_iep_set_compensation_inc(iep, iep->def_inc); 76762306a36Sopenharmony_ci icss_iep_set_compensation_count(iep, 0); 76862306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */ 76962306a36Sopenharmony_ci regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0); 77062306a36Sopenharmony_ci if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT) 77162306a36Sopenharmony_ci icss_iep_set_slow_compensation_count(iep, 0); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (!(iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) || 77462306a36Sopenharmony_ci !(iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)) 77562306a36Sopenharmony_ci goto skip_perout; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (iep->ops && iep->ops->perout_enable) { 77862306a36Sopenharmony_ci iep->ptp_info.n_per_out = 1; 77962306a36Sopenharmony_ci iep->ptp_info.pps = 1; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (iep->ops && iep->ops->extts_enable) 78362306a36Sopenharmony_ci iep->ptp_info.n_ext_ts = 2; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciskip_perout: 78662306a36Sopenharmony_ci if (cycle_time_ns) 78762306a36Sopenharmony_ci icss_iep_enable_shadow_mode(iep); 78862306a36Sopenharmony_ci else 78962306a36Sopenharmony_ci icss_iep_enable(iep); 79062306a36Sopenharmony_ci icss_iep_settime(iep, ktime_get_real_ns()); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev); 79362306a36Sopenharmony_ci if (IS_ERR(iep->ptp_clock)) { 79462306a36Sopenharmony_ci ret = PTR_ERR(iep->ptp_clock); 79562306a36Sopenharmony_ci iep->ptp_clock = NULL; 79662306a36Sopenharmony_ci dev_err(iep->dev, "Failed to register ptp clk %d\n", ret); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return ret; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_init); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ciint icss_iep_exit(struct icss_iep *iep) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci if (iep->ptp_clock) { 80662306a36Sopenharmony_ci ptp_clock_unregister(iep->ptp_clock); 80762306a36Sopenharmony_ci iep->ptp_clock = NULL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci icss_iep_disable(iep); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(icss_iep_exit); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int icss_iep_probe(struct platform_device *pdev) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 81862306a36Sopenharmony_ci struct icss_iep *iep; 81962306a36Sopenharmony_ci struct clk *iep_clk; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL); 82262306a36Sopenharmony_ci if (!iep) 82362306a36Sopenharmony_ci return -ENOMEM; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci iep->dev = dev; 82662306a36Sopenharmony_ci iep->base = devm_platform_ioremap_resource(pdev, 0); 82762306a36Sopenharmony_ci if (IS_ERR(iep->base)) 82862306a36Sopenharmony_ci return -ENODEV; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci iep_clk = devm_clk_get(dev, NULL); 83162306a36Sopenharmony_ci if (IS_ERR(iep_clk)) 83262306a36Sopenharmony_ci return PTR_ERR(iep_clk); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci iep->refclk_freq = clk_get_rate(iep_clk); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci iep->def_inc = NSEC_PER_SEC / iep->refclk_freq; /* ns per clock tick */ 83762306a36Sopenharmony_ci if (iep->def_inc > IEP_MAX_DEF_INC) { 83862306a36Sopenharmony_ci dev_err(dev, "Failed to set def_inc %d. IEP_clock is too slow to be supported\n", 83962306a36Sopenharmony_ci iep->def_inc); 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci iep->plat_data = device_get_match_data(dev); 84462306a36Sopenharmony_ci if (!iep->plat_data) 84562306a36Sopenharmony_ci return -EINVAL; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci iep->map = devm_regmap_init(dev, NULL, iep, iep->plat_data->config); 84862306a36Sopenharmony_ci if (IS_ERR(iep->map)) { 84962306a36Sopenharmony_ci dev_err(dev, "Failed to create regmap for IEP %ld\n", 85062306a36Sopenharmony_ci PTR_ERR(iep->map)); 85162306a36Sopenharmony_ci return PTR_ERR(iep->map); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci iep->ptp_info = icss_iep_ptp_info; 85562306a36Sopenharmony_ci mutex_init(&iep->ptp_clk_mutex); 85662306a36Sopenharmony_ci spin_lock_init(&iep->irq_lock); 85762306a36Sopenharmony_ci dev_set_drvdata(dev, iep); 85862306a36Sopenharmony_ci icss_iep_disable(iep); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic bool am654_icss_iep_valid_reg(struct device *dev, unsigned int reg) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci switch (reg) { 86662306a36Sopenharmony_ci case ICSS_IEP_GLOBAL_CFG_REG ... ICSS_IEP_SYNC_START_REG: 86762306a36Sopenharmony_ci return true; 86862306a36Sopenharmony_ci default: 86962306a36Sopenharmony_ci return false; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return false; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int icss_iep_regmap_write(void *context, unsigned int reg, 87662306a36Sopenharmony_ci unsigned int val) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct icss_iep *iep = context; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci writel(val, iep->base + iep->plat_data->reg_offs[reg]); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int icss_iep_regmap_read(void *context, unsigned int reg, 88662306a36Sopenharmony_ci unsigned int *val) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct icss_iep *iep = context; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci *val = readl(iep->base + iep->plat_data->reg_offs[reg]); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic struct regmap_config am654_icss_iep_regmap_config = { 89662306a36Sopenharmony_ci .name = "icss iep", 89762306a36Sopenharmony_ci .reg_stride = 1, 89862306a36Sopenharmony_ci .reg_write = icss_iep_regmap_write, 89962306a36Sopenharmony_ci .reg_read = icss_iep_regmap_read, 90062306a36Sopenharmony_ci .writeable_reg = am654_icss_iep_valid_reg, 90162306a36Sopenharmony_ci .readable_reg = am654_icss_iep_valid_reg, 90262306a36Sopenharmony_ci .fast_io = 1, 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic const struct icss_iep_plat_data am654_icss_iep_plat_data = { 90662306a36Sopenharmony_ci .flags = ICSS_IEP_64BIT_COUNTER_SUPPORT | 90762306a36Sopenharmony_ci ICSS_IEP_SLOW_COMPEN_REG_SUPPORT | 90862306a36Sopenharmony_ci ICSS_IEP_SHADOW_MODE_SUPPORT, 90962306a36Sopenharmony_ci .reg_offs = { 91062306a36Sopenharmony_ci [ICSS_IEP_GLOBAL_CFG_REG] = 0x00, 91162306a36Sopenharmony_ci [ICSS_IEP_COMPEN_REG] = 0x08, 91262306a36Sopenharmony_ci [ICSS_IEP_SLOW_COMPEN_REG] = 0x0C, 91362306a36Sopenharmony_ci [ICSS_IEP_COUNT_REG0] = 0x10, 91462306a36Sopenharmony_ci [ICSS_IEP_COUNT_REG1] = 0x14, 91562306a36Sopenharmony_ci [ICSS_IEP_CAPTURE_CFG_REG] = 0x18, 91662306a36Sopenharmony_ci [ICSS_IEP_CAPTURE_STAT_REG] = 0x1c, 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci [ICSS_IEP_CAP6_RISE_REG0] = 0x50, 91962306a36Sopenharmony_ci [ICSS_IEP_CAP6_RISE_REG1] = 0x54, 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci [ICSS_IEP_CAP7_RISE_REG0] = 0x60, 92262306a36Sopenharmony_ci [ICSS_IEP_CAP7_RISE_REG1] = 0x64, 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci [ICSS_IEP_CMP_CFG_REG] = 0x70, 92562306a36Sopenharmony_ci [ICSS_IEP_CMP_STAT_REG] = 0x74, 92662306a36Sopenharmony_ci [ICSS_IEP_CMP0_REG0] = 0x78, 92762306a36Sopenharmony_ci [ICSS_IEP_CMP0_REG1] = 0x7c, 92862306a36Sopenharmony_ci [ICSS_IEP_CMP1_REG0] = 0x80, 92962306a36Sopenharmony_ci [ICSS_IEP_CMP1_REG1] = 0x84, 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci [ICSS_IEP_CMP8_REG0] = 0xc0, 93262306a36Sopenharmony_ci [ICSS_IEP_CMP8_REG1] = 0xc4, 93362306a36Sopenharmony_ci [ICSS_IEP_SYNC_CTRL_REG] = 0x180, 93462306a36Sopenharmony_ci [ICSS_IEP_SYNC0_STAT_REG] = 0x188, 93562306a36Sopenharmony_ci [ICSS_IEP_SYNC1_STAT_REG] = 0x18c, 93662306a36Sopenharmony_ci [ICSS_IEP_SYNC_PWIDTH_REG] = 0x190, 93762306a36Sopenharmony_ci [ICSS_IEP_SYNC0_PERIOD_REG] = 0x194, 93862306a36Sopenharmony_ci [ICSS_IEP_SYNC1_DELAY_REG] = 0x198, 93962306a36Sopenharmony_ci [ICSS_IEP_SYNC_START_REG] = 0x19c, 94062306a36Sopenharmony_ci }, 94162306a36Sopenharmony_ci .config = &am654_icss_iep_regmap_config, 94262306a36Sopenharmony_ci}; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic const struct of_device_id icss_iep_of_match[] = { 94562306a36Sopenharmony_ci { 94662306a36Sopenharmony_ci .compatible = "ti,am654-icss-iep", 94762306a36Sopenharmony_ci .data = &am654_icss_iep_plat_data, 94862306a36Sopenharmony_ci }, 94962306a36Sopenharmony_ci {}, 95062306a36Sopenharmony_ci}; 95162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, icss_iep_of_match); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic struct platform_driver icss_iep_driver = { 95462306a36Sopenharmony_ci .driver = { 95562306a36Sopenharmony_ci .name = "icss-iep", 95662306a36Sopenharmony_ci .of_match_table = icss_iep_of_match, 95762306a36Sopenharmony_ci }, 95862306a36Sopenharmony_ci .probe = icss_iep_probe, 95962306a36Sopenharmony_ci}; 96062306a36Sopenharmony_cimodule_platform_driver(icss_iep_driver); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 96362306a36Sopenharmony_ciMODULE_DESCRIPTION("TI ICSS IEP driver"); 96462306a36Sopenharmony_ciMODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); 96562306a36Sopenharmony_ciMODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>"); 966