954 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver4 *5 * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com6 *7 */8 9#include <linux/bitops.h>10#include <linux/clk.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/of_platform.h>16#include <linux/platform_device.h>17#include <linux/timekeeping.h>18#include <linux/interrupt.h>19#include <linux/of_irq.h>20#include <linux/workqueue.h>21 22#include "icss_iep.h"23 24#define IEP_MAX_DEF_INC 0xf25#define IEP_MAX_COMPEN_INC 0xfff26#define IEP_MAX_COMPEN_COUNT 0xffffff27 28#define IEP_GLOBAL_CFG_CNT_ENABLE BIT(0)29#define IEP_GLOBAL_CFG_DEFAULT_INC_MASK GENMASK(7, 4)30#define IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT 431#define IEP_GLOBAL_CFG_COMPEN_INC_MASK GENMASK(19, 8)32#define IEP_GLOBAL_CFG_COMPEN_INC_SHIFT 833 34#define IEP_GLOBAL_STATUS_CNT_OVF BIT(0)35 36#define IEP_CMP_CFG_SHADOW_EN BIT(17)37#define IEP_CMP_CFG_CMP0_RST_CNT_EN BIT(0)38#define IEP_CMP_CFG_CMP_EN(cmp) (GENMASK(16, 1) & (1 << ((cmp) + 1)))39 40#define IEP_CMP_STATUS(cmp) (1 << (cmp))41 42#define IEP_SYNC_CTRL_SYNC_EN BIT(0)43#define IEP_SYNC_CTRL_SYNC_N_EN(n) (GENMASK(2, 1) & (BIT(1) << (n)))44 45#define IEP_MIN_CMP 046#define IEP_MAX_CMP 1547 48#define ICSS_IEP_64BIT_COUNTER_SUPPORT BIT(0)49#define ICSS_IEP_SLOW_COMPEN_REG_SUPPORT BIT(1)50#define ICSS_IEP_SHADOW_MODE_SUPPORT BIT(2)51 52#define LATCH_INDEX(ts_index) ((ts_index) + 6)53#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n))54#define IEP_CAP_CFG_CAP_ASYNC_EN(n) BIT(LATCH_INDEX(n) + 10)55 56/**57 * icss_iep_get_count_hi() - Get the upper 32 bit IEP counter58 * @iep: Pointer to structure representing IEP.59 *60 * Return: upper 32 bit IEP counter61 */62int icss_iep_get_count_hi(struct icss_iep *iep)63{64 u32 val = 0;65 66 if (iep && (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT))67 val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);68 69 return val;70}71EXPORT_SYMBOL_GPL(icss_iep_get_count_hi);72 73/**74 * icss_iep_get_count_low() - Get the lower 32 bit IEP counter75 * @iep: Pointer to structure representing IEP.76 *77 * Return: lower 32 bit IEP counter78 */79int icss_iep_get_count_low(struct icss_iep *iep)80{81 u32 val = 0;82 83 if (iep)84 val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);85 86 return val;87}88EXPORT_SYMBOL_GPL(icss_iep_get_count_low);89 90/**91 * icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver92 * @iep: Pointer to structure representing IEP.93 *94 * Return: PTP clock index, -1 if not registered95 */96int icss_iep_get_ptp_clock_idx(struct icss_iep *iep)97{98 if (!iep || !iep->ptp_clock)99 return -1;100 return ptp_clock_index(iep->ptp_clock);101}102EXPORT_SYMBOL_GPL(icss_iep_get_ptp_clock_idx);103 104static void icss_iep_set_counter(struct icss_iep *iep, u64 ns)105{106 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)107 writel(upper_32_bits(ns), iep->base +108 iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);109 writel(lower_32_bits(ns), iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);110}111 112static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns);113 114/**115 * icss_iep_settime() - Set time of the PTP clock using IEP driver116 * @iep: Pointer to structure representing IEP.117 * @ns: Time to be set in nanoseconds118 *119 * This API uses writel() instead of regmap_write() for write operations as120 * regmap_write() is too slow and this API is time sensitive.121 */122static void icss_iep_settime(struct icss_iep *iep, u64 ns)123{124 if (iep->ops && iep->ops->settime) {125 iep->ops->settime(iep->clockops_data, ns);126 return;127 }128 129 if (iep->pps_enabled || iep->perout_enabled)130 writel(0, iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);131 132 icss_iep_set_counter(iep, ns);133 134 if (iep->pps_enabled || iep->perout_enabled) {135 icss_iep_update_to_next_boundary(iep, ns);136 writel(IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN,137 iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);138 }139}140 141/**142 * icss_iep_gettime() - Get time of the PTP clock using IEP driver143 * @iep: Pointer to structure representing IEP.144 * @sts: Pointer to structure representing PTP system timestamp.145 *146 * This API uses readl() instead of regmap_read() for read operations as147 * regmap_read() is too slow and this API is time sensitive.148 *149 * Return: The current timestamp of the PTP clock using IEP driver150 */151static u64 icss_iep_gettime(struct icss_iep *iep,152 struct ptp_system_timestamp *sts)153{154 u32 ts_hi = 0, ts_lo;155 unsigned long flags;156 157 if (iep->ops && iep->ops->gettime)158 return iep->ops->gettime(iep->clockops_data, sts);159 160 /* use local_irq_x() to make it work for both RT/non-RT */161 local_irq_save(flags);162 163 /* no need to play with hi-lo, hi is latched when lo is read */164 ptp_read_system_prets(sts);165 ts_lo = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);166 ptp_read_system_postts(sts);167 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)168 ts_hi = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);169 170 local_irq_restore(flags);171 172 return (u64)ts_lo | (u64)ts_hi << 32;173}174 175static void icss_iep_enable(struct icss_iep *iep)176{177 regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,178 IEP_GLOBAL_CFG_CNT_ENABLE,179 IEP_GLOBAL_CFG_CNT_ENABLE);180}181 182static void icss_iep_disable(struct icss_iep *iep)183{184 regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,185 IEP_GLOBAL_CFG_CNT_ENABLE,186 0);187}188 189static void icss_iep_enable_shadow_mode(struct icss_iep *iep)190{191 u32 cycle_time;192 int cmp;193 194 cycle_time = iep->cycle_time_ns - iep->def_inc;195 196 icss_iep_disable(iep);197 198 /* disable shadow mode */199 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,200 IEP_CMP_CFG_SHADOW_EN, 0);201 202 /* enable shadow mode */203 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,204 IEP_CMP_CFG_SHADOW_EN, IEP_CMP_CFG_SHADOW_EN);205 206 /* clear counters */207 icss_iep_set_counter(iep, 0);208 209 /* clear overflow status */210 regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_STATUS_REG,211 IEP_GLOBAL_STATUS_CNT_OVF,212 IEP_GLOBAL_STATUS_CNT_OVF);213 214 /* clear compare status */215 for (cmp = IEP_MIN_CMP; cmp < IEP_MAX_CMP; cmp++) {216 regmap_update_bits(iep->map, ICSS_IEP_CMP_STAT_REG,217 IEP_CMP_STATUS(cmp), IEP_CMP_STATUS(cmp));218 }219 220 /* enable reset counter on CMP0 event */221 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,222 IEP_CMP_CFG_CMP0_RST_CNT_EN,223 IEP_CMP_CFG_CMP0_RST_CNT_EN);224 /* enable compare */225 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,226 IEP_CMP_CFG_CMP_EN(0),227 IEP_CMP_CFG_CMP_EN(0));228 229 /* set CMP0 value to cycle time */230 regmap_write(iep->map, ICSS_IEP_CMP0_REG0, cycle_time);231 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)232 regmap_write(iep->map, ICSS_IEP_CMP0_REG1, cycle_time);233 234 icss_iep_set_counter(iep, 0);235 icss_iep_enable(iep);236}237 238static void icss_iep_set_default_inc(struct icss_iep *iep, u8 def_inc)239{240 regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,241 IEP_GLOBAL_CFG_DEFAULT_INC_MASK,242 def_inc << IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT);243}244 245static void icss_iep_set_compensation_inc(struct icss_iep *iep, u16 compen_inc)246{247 struct device *dev = regmap_get_device(iep->map);248 249 if (compen_inc > IEP_MAX_COMPEN_INC) {250 dev_err(dev, "%s: too high compensation inc %d\n",251 __func__, compen_inc);252 compen_inc = IEP_MAX_COMPEN_INC;253 }254 255 regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,256 IEP_GLOBAL_CFG_COMPEN_INC_MASK,257 compen_inc << IEP_GLOBAL_CFG_COMPEN_INC_SHIFT);258}259 260static void icss_iep_set_compensation_count(struct icss_iep *iep,261 u32 compen_count)262{263 struct device *dev = regmap_get_device(iep->map);264 265 if (compen_count > IEP_MAX_COMPEN_COUNT) {266 dev_err(dev, "%s: too high compensation count %d\n",267 __func__, compen_count);268 compen_count = IEP_MAX_COMPEN_COUNT;269 }270 271 regmap_write(iep->map, ICSS_IEP_COMPEN_REG, compen_count);272}273 274static void icss_iep_set_slow_compensation_count(struct icss_iep *iep,275 u32 compen_count)276{277 regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count);278}279 280/* PTP PHC operations */281static int icss_iep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)282{283 struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);284 s32 ppb = scaled_ppm_to_ppb(scaled_ppm);285 u32 cyc_count;286 u16 cmp_inc;287 288 mutex_lock(&iep->ptp_clk_mutex);289 290 /* ppb is amount of frequency we want to adjust in 1GHz (billion)291 * e.g. 100ppb means we need to speed up clock by 100Hz292 * i.e. at end of 1 second (1 billion ns) clock time, we should be293 * counting 100 more ns.294 * We use IEP slow compensation to achieve continuous freq. adjustment.295 * There are 2 parts. Cycle time and adjustment per cycle.296 * Simplest case would be 1 sec Cycle time. Then adjustment297 * pre cycle would be (def_inc + ppb) value.298 * Cycle time will have to be chosen based on how worse the ppb is.299 * e.g. smaller the ppb, cycle time has to be large.300 * The minimum adjustment we can do is +-1ns per cycle so let's301 * reduce the cycle time to get 1ns per cycle adjustment.302 * 1ppb = 1sec cycle time & 1ns adjust303 * 1000ppb = 1/1000 cycle time & 1ns adjust per cycle304 */305 306 if (iep->cycle_time_ns)307 iep->slow_cmp_inc = iep->clk_tick_time; /* 4ns adj per cycle */308 else309 iep->slow_cmp_inc = 1; /* 1ns adjust per cycle */310 311 if (ppb < 0) {312 iep->slow_cmp_inc = -iep->slow_cmp_inc;313 ppb = -ppb;314 }315 316 cyc_count = NSEC_PER_SEC; /* 1s cycle time @1GHz */317 cyc_count /= ppb; /* cycle time per ppb */318 319 /* slow_cmp_count is decremented every clock cycle, e.g. @250MHz */320 if (!iep->cycle_time_ns)321 cyc_count /= iep->clk_tick_time;322 iep->slow_cmp_count = cyc_count;323 324 /* iep->clk_tick_time is def_inc */325 cmp_inc = iep->clk_tick_time + iep->slow_cmp_inc;326 icss_iep_set_compensation_inc(iep, cmp_inc);327 icss_iep_set_slow_compensation_count(iep, iep->slow_cmp_count);328 329 mutex_unlock(&iep->ptp_clk_mutex);330 331 return 0;332}333 334static int icss_iep_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)335{336 struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);337 s64 ns;338 339 mutex_lock(&iep->ptp_clk_mutex);340 if (iep->ops && iep->ops->adjtime) {341 iep->ops->adjtime(iep->clockops_data, delta);342 } else {343 ns = icss_iep_gettime(iep, NULL);344 ns += delta;345 icss_iep_settime(iep, ns);346 }347 mutex_unlock(&iep->ptp_clk_mutex);348 349 return 0;350}351 352static int icss_iep_ptp_gettimeex(struct ptp_clock_info *ptp,353 struct timespec64 *ts,354 struct ptp_system_timestamp *sts)355{356 struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);357 u64 ns;358 359 mutex_lock(&iep->ptp_clk_mutex);360 ns = icss_iep_gettime(iep, sts);361 *ts = ns_to_timespec64(ns);362 mutex_unlock(&iep->ptp_clk_mutex);363 364 return 0;365}366 367static int icss_iep_ptp_settime(struct ptp_clock_info *ptp,368 const struct timespec64 *ts)369{370 struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);371 u64 ns;372 373 mutex_lock(&iep->ptp_clk_mutex);374 ns = timespec64_to_ns(ts);375 icss_iep_settime(iep, ns);376 mutex_unlock(&iep->ptp_clk_mutex);377 378 return 0;379}380 381static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns)382{383 u64 ns, p_ns;384 u32 offset;385 386 ns = icss_iep_gettime(iep, NULL);387 if (start_ns < ns)388 start_ns = ns;389 p_ns = iep->period;390 /* Round up to next period boundary */391 start_ns += p_ns - 1;392 offset = do_div(start_ns, p_ns);393 start_ns = start_ns * p_ns;394 /* If it is too close to update, shift to next boundary */395 if (p_ns - offset < 10)396 start_ns += p_ns;397 398 regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(start_ns));399 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)400 regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(start_ns));401}402 403static int icss_iep_perout_enable_hw(struct icss_iep *iep,404 struct ptp_perout_request *req, int on)405{406 int ret;407 u64 cmp;408 409 if (iep->ops && iep->ops->perout_enable) {410 ret = iep->ops->perout_enable(iep->clockops_data, req, on, &cmp);411 if (ret)412 return ret;413 414 if (on) {415 /* Configure CMP */416 regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp));417 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)418 regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp));419 /* Configure SYNC, 1ms pulse width */420 regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, 1000000);421 regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);422 regmap_write(iep->map, ICSS_IEP_SYNC_START_REG, 0);423 regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */424 /* Enable CMP 1 */425 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,426 IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));427 } else {428 /* Disable CMP 1 */429 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,430 IEP_CMP_CFG_CMP_EN(1), 0);431 432 /* clear regs */433 regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);434 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)435 regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);436 }437 } else {438 if (on) {439 u64 start_ns;440 441 iep->period = ((u64)req->period.sec * NSEC_PER_SEC) +442 req->period.nsec;443 start_ns = ((u64)req->period.sec * NSEC_PER_SEC)444 + req->period.nsec;445 icss_iep_update_to_next_boundary(iep, start_ns);446 447 /* Enable Sync in single shot mode */448 regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG,449 IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN);450 /* Enable CMP 1 */451 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,452 IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));453 } else {454 /* Disable CMP 1 */455 regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,456 IEP_CMP_CFG_CMP_EN(1), 0);457 458 /* clear CMP regs */459 regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);460 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)461 regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);462 463 /* Disable sync */464 regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0);465 }466 }467 468 return 0;469}470 471static int icss_iep_perout_enable(struct icss_iep *iep,472 struct ptp_perout_request *req, int on)473{474 int ret = 0;475 476 mutex_lock(&iep->ptp_clk_mutex);477 478 if (iep->pps_enabled) {479 ret = -EBUSY;480 goto exit;481 }482 483 if (iep->perout_enabled == !!on)484 goto exit;485 486 ret = icss_iep_perout_enable_hw(iep, req, on);487 if (!ret)488 iep->perout_enabled = !!on;489 490exit:491 mutex_unlock(&iep->ptp_clk_mutex);492 493 return ret;494}495 496static void icss_iep_cap_cmp_work(struct work_struct *work)497{498 struct icss_iep *iep = container_of(work, struct icss_iep, work);499 const u32 *reg_offs = iep->plat_data->reg_offs;500 struct ptp_clock_event pevent;501 unsigned int val;502 u64 ns, ns_next;503 504 mutex_lock(&iep->ptp_clk_mutex);505 506 ns = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);507 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {508 val = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);509 ns |= (u64)val << 32;510 }511 /* set next event */512 ns_next = ns + iep->period;513 writel(lower_32_bits(ns_next),514 iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);515 if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)516 writel(upper_32_bits(ns_next),517 iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);518 519 pevent.pps_times.ts_real = ns_to_timespec64(ns);520 pevent.type = PTP_CLOCK_PPSUSR;521 pevent.index = 0;522 ptp_clock_event(iep->ptp_clock, &pevent);523 dev_dbg(iep->dev, "IEP:pps ts: %llu next:%llu:\n", ns, ns_next);524 525 mutex_unlock(&iep->ptp_clk_mutex);526}527 528static irqreturn_t icss_iep_cap_cmp_irq(int irq, void *dev_id)529{530 struct icss_iep *iep = (struct icss_iep *)dev_id;531 const u32 *reg_offs = iep->plat_data->reg_offs;532 unsigned int val;533 534 val = readl(iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);535 /* The driver only enables CMP1 */536 if (val & BIT(1)) {537 /* Clear the event */538 writel(BIT(1), iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);539 if (iep->pps_enabled || iep->perout_enabled)540 schedule_work(&iep->work);541 return IRQ_HANDLED;542 }543 544 return IRQ_NONE;545}546 547static int icss_iep_pps_enable(struct icss_iep *iep, int on)548{549 struct ptp_clock_request rq;550 struct timespec64 ts;551 int ret = 0;552 u64 ns;553 554 mutex_lock(&iep->ptp_clk_mutex);555 556 if (iep->perout_enabled) {557 ret = -EBUSY;558 goto exit;559 }560 561 if (iep->pps_enabled == !!on)562 goto exit;563 564 rq.perout.index = 0;565 if (on) {566 ns = icss_iep_gettime(iep, NULL);567 ts = ns_to_timespec64(ns);568 rq.perout.period.sec = 1;569 rq.perout.period.nsec = 0;570 rq.perout.start.sec = ts.tv_sec + 2;571 rq.perout.start.nsec = 0;572 ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);573 } else {574 ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);575 if (iep->cap_cmp_irq)576 cancel_work_sync(&iep->work);577 }578 579 if (!ret)580 iep->pps_enabled = !!on;581 582exit:583 mutex_unlock(&iep->ptp_clk_mutex);584 585 return ret;586}587 588static int icss_iep_extts_enable(struct icss_iep *iep, u32 index, int on)589{590 u32 val, cap, ret = 0;591 592 mutex_lock(&iep->ptp_clk_mutex);593 594 if (iep->ops && iep->ops->extts_enable) {595 ret = iep->ops->extts_enable(iep->clockops_data, index, on);596 goto exit;597 }598 599 if (((iep->latch_enable & BIT(index)) >> index) == on)600 goto exit;601 602 regmap_read(iep->map, ICSS_IEP_CAPTURE_CFG_REG, &val);603 cap = IEP_CAP_CFG_CAP_ASYNC_EN(index) | IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(index);604 if (on) {605 val |= cap;606 iep->latch_enable |= BIT(index);607 } else {608 val &= ~cap;609 iep->latch_enable &= ~BIT(index);610 }611 regmap_write(iep->map, ICSS_IEP_CAPTURE_CFG_REG, val);612 613exit:614 mutex_unlock(&iep->ptp_clk_mutex);615 616 return ret;617}618 619static int icss_iep_ptp_enable(struct ptp_clock_info *ptp,620 struct ptp_clock_request *rq, int on)621{622 struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);623 624 switch (rq->type) {625 case PTP_CLK_REQ_PEROUT:626 return icss_iep_perout_enable(iep, &rq->perout, on);627 case PTP_CLK_REQ_PPS:628 return icss_iep_pps_enable(iep, on);629 case PTP_CLK_REQ_EXTTS:630 return icss_iep_extts_enable(iep, rq->extts.index, on);631 default:632 break;633 }634 635 return -EOPNOTSUPP;636}637 638static struct ptp_clock_info icss_iep_ptp_info = {639 .owner = THIS_MODULE,640 .name = "ICSS IEP timer",641 .max_adj = 10000000,642 .adjfine = icss_iep_ptp_adjfine,643 .adjtime = icss_iep_ptp_adjtime,644 .gettimex64 = icss_iep_ptp_gettimeex,645 .settime64 = icss_iep_ptp_settime,646 .enable = icss_iep_ptp_enable,647};648 649struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)650{651 struct platform_device *pdev;652 struct device_node *iep_np;653 struct icss_iep *iep;654 655 iep_np = of_parse_phandle(np, "ti,iep", idx);656 if (!iep_np || !of_device_is_available(iep_np))657 return ERR_PTR(-ENODEV);658 659 pdev = of_find_device_by_node(iep_np);660 of_node_put(iep_np);661 662 if (!pdev)663 /* probably IEP not yet probed */664 return ERR_PTR(-EPROBE_DEFER);665 666 iep = platform_get_drvdata(pdev);667 if (!iep)668 return ERR_PTR(-EPROBE_DEFER);669 670 device_lock(iep->dev);671 if (iep->client_np) {672 device_unlock(iep->dev);673 dev_err(iep->dev, "IEP is already acquired by %s",674 iep->client_np->name);675 return ERR_PTR(-EBUSY);676 }677 iep->client_np = np;678 device_unlock(iep->dev);679 get_device(iep->dev);680 681 return iep;682}683EXPORT_SYMBOL_GPL(icss_iep_get_idx);684 685struct icss_iep *icss_iep_get(struct device_node *np)686{687 return icss_iep_get_idx(np, 0);688}689EXPORT_SYMBOL_GPL(icss_iep_get);690 691void icss_iep_put(struct icss_iep *iep)692{693 device_lock(iep->dev);694 iep->client_np = NULL;695 device_unlock(iep->dev);696 put_device(iep->dev);697}698EXPORT_SYMBOL_GPL(icss_iep_put);699 700void icss_iep_init_fw(struct icss_iep *iep)701{702 /* start IEP for FW use in raw 64bit mode, no PTP support */703 iep->clk_tick_time = iep->def_inc;704 iep->cycle_time_ns = 0;705 iep->ops = NULL;706 iep->clockops_data = NULL;707 icss_iep_set_default_inc(iep, iep->def_inc);708 icss_iep_set_compensation_inc(iep, iep->def_inc);709 icss_iep_set_compensation_count(iep, 0);710 regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */711 regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);712 if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)713 icss_iep_set_slow_compensation_count(iep, 0);714 715 icss_iep_enable(iep);716 icss_iep_settime(iep, 0);717}718EXPORT_SYMBOL_GPL(icss_iep_init_fw);719 720void icss_iep_exit_fw(struct icss_iep *iep)721{722 icss_iep_disable(iep);723}724EXPORT_SYMBOL_GPL(icss_iep_exit_fw);725 726int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,727 void *clockops_data, u32 cycle_time_ns)728{729 int ret = 0;730 731 iep->cycle_time_ns = cycle_time_ns;732 iep->clk_tick_time = iep->def_inc;733 iep->ops = clkops;734 iep->clockops_data = clockops_data;735 icss_iep_set_default_inc(iep, iep->def_inc);736 icss_iep_set_compensation_inc(iep, iep->def_inc);737 icss_iep_set_compensation_count(iep, 0);738 regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */739 regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);740 if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)741 icss_iep_set_slow_compensation_count(iep, 0);742 743 if (!(iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) ||744 !(iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT))745 goto skip_perout;746 747 if (iep->ops && iep->ops->perout_enable) {748 iep->ptp_info.n_per_out = 1;749 iep->ptp_info.pps = 1;750 } else if (iep->cap_cmp_irq) {751 iep->ptp_info.pps = 1;752 }753 754 if (iep->ops && iep->ops->extts_enable)755 iep->ptp_info.n_ext_ts = 2;756 757skip_perout:758 if (cycle_time_ns)759 icss_iep_enable_shadow_mode(iep);760 else761 icss_iep_enable(iep);762 icss_iep_settime(iep, ktime_get_real_ns());763 764 iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev);765 if (IS_ERR(iep->ptp_clock)) {766 ret = PTR_ERR(iep->ptp_clock);767 iep->ptp_clock = NULL;768 dev_err(iep->dev, "Failed to register ptp clk %d\n", ret);769 }770 771 return ret;772}773EXPORT_SYMBOL_GPL(icss_iep_init);774 775int icss_iep_exit(struct icss_iep *iep)776{777 if (iep->ptp_clock) {778 ptp_clock_unregister(iep->ptp_clock);779 iep->ptp_clock = NULL;780 }781 icss_iep_disable(iep);782 783 return 0;784}785EXPORT_SYMBOL_GPL(icss_iep_exit);786 787static int icss_iep_probe(struct platform_device *pdev)788{789 struct device *dev = &pdev->dev;790 struct icss_iep *iep;791 struct clk *iep_clk;792 int ret, irq;793 794 iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);795 if (!iep)796 return -ENOMEM;797 798 iep->dev = dev;799 iep->base = devm_platform_ioremap_resource(pdev, 0);800 if (IS_ERR(iep->base))801 return -ENODEV;802 803 irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp");804 if (irq == -EPROBE_DEFER)805 return irq;806 807 if (irq > 0) {808 ret = devm_request_irq(dev, irq, icss_iep_cap_cmp_irq,809 IRQF_TRIGGER_HIGH, "iep_cap_cmp", iep);810 if (ret) {811 dev_info(iep->dev, "cap_cmp irq request failed: %x\n",812 ret);813 } else {814 iep->cap_cmp_irq = irq;815 INIT_WORK(&iep->work, icss_iep_cap_cmp_work);816 }817 }818 819 iep_clk = devm_clk_get(dev, NULL);820 if (IS_ERR(iep_clk))821 return PTR_ERR(iep_clk);822 823 iep->refclk_freq = clk_get_rate(iep_clk);824 825 iep->def_inc = NSEC_PER_SEC / iep->refclk_freq; /* ns per clock tick */826 if (iep->def_inc > IEP_MAX_DEF_INC) {827 dev_err(dev, "Failed to set def_inc %d. IEP_clock is too slow to be supported\n",828 iep->def_inc);829 return -EINVAL;830 }831 832 iep->plat_data = device_get_match_data(dev);833 if (!iep->plat_data)834 return -EINVAL;835 836 iep->map = devm_regmap_init(dev, NULL, iep, iep->plat_data->config);837 if (IS_ERR(iep->map)) {838 dev_err(dev, "Failed to create regmap for IEP %ld\n",839 PTR_ERR(iep->map));840 return PTR_ERR(iep->map);841 }842 843 iep->ptp_info = icss_iep_ptp_info;844 mutex_init(&iep->ptp_clk_mutex);845 dev_set_drvdata(dev, iep);846 icss_iep_disable(iep);847 848 return 0;849}850 851static bool am654_icss_iep_valid_reg(struct device *dev, unsigned int reg)852{853 switch (reg) {854 case ICSS_IEP_GLOBAL_CFG_REG ... ICSS_IEP_SYNC_START_REG:855 return true;856 default:857 return false;858 }859 860 return false;861}862 863static int icss_iep_regmap_write(void *context, unsigned int reg,864 unsigned int val)865{866 struct icss_iep *iep = context;867 868 writel(val, iep->base + iep->plat_data->reg_offs[reg]);869 870 return 0;871}872 873static int icss_iep_regmap_read(void *context, unsigned int reg,874 unsigned int *val)875{876 struct icss_iep *iep = context;877 878 *val = readl(iep->base + iep->plat_data->reg_offs[reg]);879 880 return 0;881}882 883static const struct regmap_config am654_icss_iep_regmap_config = {884 .name = "icss iep",885 .reg_stride = 1,886 .reg_write = icss_iep_regmap_write,887 .reg_read = icss_iep_regmap_read,888 .writeable_reg = am654_icss_iep_valid_reg,889 .readable_reg = am654_icss_iep_valid_reg,890 .fast_io = 1,891};892 893static const struct icss_iep_plat_data am654_icss_iep_plat_data = {894 .flags = ICSS_IEP_64BIT_COUNTER_SUPPORT |895 ICSS_IEP_SLOW_COMPEN_REG_SUPPORT |896 ICSS_IEP_SHADOW_MODE_SUPPORT,897 .reg_offs = {898 [ICSS_IEP_GLOBAL_CFG_REG] = 0x00,899 [ICSS_IEP_COMPEN_REG] = 0x08,900 [ICSS_IEP_SLOW_COMPEN_REG] = 0x0C,901 [ICSS_IEP_COUNT_REG0] = 0x10,902 [ICSS_IEP_COUNT_REG1] = 0x14,903 [ICSS_IEP_CAPTURE_CFG_REG] = 0x18,904 [ICSS_IEP_CAPTURE_STAT_REG] = 0x1c,905 906 [ICSS_IEP_CAP6_RISE_REG0] = 0x50,907 [ICSS_IEP_CAP6_RISE_REG1] = 0x54,908 909 [ICSS_IEP_CAP7_RISE_REG0] = 0x60,910 [ICSS_IEP_CAP7_RISE_REG1] = 0x64,911 912 [ICSS_IEP_CMP_CFG_REG] = 0x70,913 [ICSS_IEP_CMP_STAT_REG] = 0x74,914 [ICSS_IEP_CMP0_REG0] = 0x78,915 [ICSS_IEP_CMP0_REG1] = 0x7c,916 [ICSS_IEP_CMP1_REG0] = 0x80,917 [ICSS_IEP_CMP1_REG1] = 0x84,918 919 [ICSS_IEP_CMP8_REG0] = 0xc0,920 [ICSS_IEP_CMP8_REG1] = 0xc4,921 [ICSS_IEP_SYNC_CTRL_REG] = 0x180,922 [ICSS_IEP_SYNC0_STAT_REG] = 0x188,923 [ICSS_IEP_SYNC1_STAT_REG] = 0x18c,924 [ICSS_IEP_SYNC_PWIDTH_REG] = 0x190,925 [ICSS_IEP_SYNC0_PERIOD_REG] = 0x194,926 [ICSS_IEP_SYNC1_DELAY_REG] = 0x198,927 [ICSS_IEP_SYNC_START_REG] = 0x19c,928 },929 .config = &am654_icss_iep_regmap_config,930};931 932static const struct of_device_id icss_iep_of_match[] = {933 {934 .compatible = "ti,am654-icss-iep",935 .data = &am654_icss_iep_plat_data,936 },937 {},938};939MODULE_DEVICE_TABLE(of, icss_iep_of_match);940 941static struct platform_driver icss_iep_driver = {942 .driver = {943 .name = "icss-iep",944 .of_match_table = icss_iep_of_match,945 },946 .probe = icss_iep_probe,947};948module_platform_driver(icss_iep_driver);949 950MODULE_LICENSE("GPL");951MODULE_DESCRIPTION("TI ICSS IEP driver");952MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");953MODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>");954