18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (C) 2018 Integrated Device Technology, Inc 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IDT_82p33xxx: " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/firmware.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/timekeeping.h> 158c2ecf20Sopenharmony_ci#include <linux/bitops.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "ptp_private.h" 188c2ecf20Sopenharmony_ci#include "ptp_idt82p33.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for IDT 82p33xxx clock devices"); 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>"); 228c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Module Parameters */ 268c2ecf20Sopenharmony_cistatic u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC; 278c2ecf20Sopenharmony_cimodule_param(sync_tod_timeout, uint, 0); 288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sync_tod_timeout, 298c2ecf20Sopenharmony_ci"duration in second to keep SYNC_TOD on (set to 0 to keep it always on)"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic u32 phase_snap_threshold = SNAP_THRESHOLD_NS; 328c2ecf20Sopenharmony_cimodule_param(phase_snap_threshold, uint, 0); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(phase_snap_threshold, 348c2ecf20Sopenharmony_ci"threshold (150000ns by default) below which adjtime would ignore"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void idt82p33_byte_array_to_timespec(struct timespec64 *ts, 378c2ecf20Sopenharmony_ci u8 buf[TOD_BYTE_COUNT]) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci time64_t sec; 408c2ecf20Sopenharmony_ci s32 nsec; 418c2ecf20Sopenharmony_ci u8 i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci nsec = buf[3]; 448c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 458c2ecf20Sopenharmony_ci nsec <<= 8; 468c2ecf20Sopenharmony_ci nsec |= buf[2 - i]; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci sec = buf[9]; 508c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 518c2ecf20Sopenharmony_ci sec <<= 8; 528c2ecf20Sopenharmony_ci sec |= buf[8 - i]; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ts->tv_sec = sec; 568c2ecf20Sopenharmony_ci ts->tv_nsec = nsec; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void idt82p33_timespec_to_byte_array(struct timespec64 const *ts, 608c2ecf20Sopenharmony_ci u8 buf[TOD_BYTE_COUNT]) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci time64_t sec; 638c2ecf20Sopenharmony_ci s32 nsec; 648c2ecf20Sopenharmony_ci u8 i; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci nsec = ts->tv_nsec; 678c2ecf20Sopenharmony_ci sec = ts->tv_sec; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 708c2ecf20Sopenharmony_ci buf[i] = nsec & 0xff; 718c2ecf20Sopenharmony_ci nsec >>= 8; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (i = 4; i < TOD_BYTE_COUNT; i++) { 758c2ecf20Sopenharmony_ci buf[i] = sec & 0xff; 768c2ecf20Sopenharmony_ci sec >>= 8; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int idt82p33_xfer(struct idt82p33 *idt82p33, 818c2ecf20Sopenharmony_ci unsigned char regaddr, 828c2ecf20Sopenharmony_ci unsigned char *buf, 838c2ecf20Sopenharmony_ci unsigned int count, 848c2ecf20Sopenharmony_ci int write) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct i2c_client *client = idt82p33->client; 878c2ecf20Sopenharmony_ci struct i2c_msg msg[2]; 888c2ecf20Sopenharmony_ci int cnt; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci msg[0].addr = client->addr; 918c2ecf20Sopenharmony_ci msg[0].flags = 0; 928c2ecf20Sopenharmony_ci msg[0].len = 1; 938c2ecf20Sopenharmony_ci msg[0].buf = ®addr; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci msg[1].addr = client->addr; 968c2ecf20Sopenharmony_ci msg[1].flags = write ? 0 : I2C_M_RD; 978c2ecf20Sopenharmony_ci msg[1].len = count; 988c2ecf20Sopenharmony_ci msg[1].buf = buf; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci cnt = i2c_transfer(client->adapter, msg, 2); 1018c2ecf20Sopenharmony_ci if (cnt < 0) { 1028c2ecf20Sopenharmony_ci dev_err(&client->dev, "i2c_transfer returned %d\n", cnt); 1038c2ecf20Sopenharmony_ci return cnt; 1048c2ecf20Sopenharmony_ci } else if (cnt != 2) { 1058c2ecf20Sopenharmony_ci dev_err(&client->dev, 1068c2ecf20Sopenharmony_ci "i2c_transfer sent only %d of %d messages\n", cnt, 2); 1078c2ecf20Sopenharmony_ci return -EIO; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int idt82p33_page_offset(struct idt82p33 *idt82p33, unsigned char val) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int err; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (idt82p33->page_offset == val) 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci err = idt82p33_xfer(idt82p33, PAGE_ADDR, &val, sizeof(val), 1); 1208c2ecf20Sopenharmony_ci if (err) 1218c2ecf20Sopenharmony_ci dev_err(&idt82p33->client->dev, 1228c2ecf20Sopenharmony_ci "failed to set page offset %d\n", val); 1238c2ecf20Sopenharmony_ci else 1248c2ecf20Sopenharmony_ci idt82p33->page_offset = val; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return err; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int idt82p33_rdwr(struct idt82p33 *idt82p33, unsigned int regaddr, 1308c2ecf20Sopenharmony_ci unsigned char *buf, unsigned int count, bool write) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u8 offset, page; 1338c2ecf20Sopenharmony_ci int err; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci page = _PAGE(regaddr); 1368c2ecf20Sopenharmony_ci offset = _OFFSET(regaddr); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = idt82p33_page_offset(idt82p33, page); 1398c2ecf20Sopenharmony_ci if (err) 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci err = idt82p33_xfer(idt82p33, offset, buf, count, write); 1438c2ecf20Sopenharmony_ciout: 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int idt82p33_read(struct idt82p33 *idt82p33, unsigned int regaddr, 1488c2ecf20Sopenharmony_ci unsigned char *buf, unsigned int count) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci return idt82p33_rdwr(idt82p33, regaddr, buf, count, false); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int idt82p33_write(struct idt82p33 *idt82p33, unsigned int regaddr, 1548c2ecf20Sopenharmony_ci unsigned char *buf, unsigned int count) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci return idt82p33_rdwr(idt82p33, regaddr, buf, count, true); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int idt82p33_dpll_set_mode(struct idt82p33_channel *channel, 1608c2ecf20Sopenharmony_ci enum pll_mode mode) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 1638c2ecf20Sopenharmony_ci u8 dpll_mode; 1648c2ecf20Sopenharmony_ci int err; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (channel->pll_mode == mode) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci err = idt82p33_read(idt82p33, channel->dpll_mode_cnfg, 1708c2ecf20Sopenharmony_ci &dpll_mode, sizeof(dpll_mode)); 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci return err; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dpll_mode |= (mode << PLL_MODE_SHIFT); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_mode_cnfg, 1798c2ecf20Sopenharmony_ci &dpll_mode, sizeof(dpll_mode)); 1808c2ecf20Sopenharmony_ci if (err) 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci channel->pll_mode = dpll_mode; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int _idt82p33_gettime(struct idt82p33_channel *channel, 1898c2ecf20Sopenharmony_ci struct timespec64 *ts) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 1928c2ecf20Sopenharmony_ci u8 buf[TOD_BYTE_COUNT]; 1938c2ecf20Sopenharmony_ci u8 trigger; 1948c2ecf20Sopenharmony_ci int err; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, 1978c2ecf20Sopenharmony_ci HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_tod_trigger, 2018c2ecf20Sopenharmony_ci &trigger, sizeof(trigger)); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (err) 2048c2ecf20Sopenharmony_ci return err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (idt82p33->calculate_overhead_flag) 2078c2ecf20Sopenharmony_ci idt82p33->start_time = ktime_get_raw(); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf)); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (err) 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci idt82p33_byte_array_to_timespec(ts, buf); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* 2208c2ecf20Sopenharmony_ci * TOD Trigger: 2218c2ecf20Sopenharmony_ci * Bits[7:4] Write 0x9, MSB write 2228c2ecf20Sopenharmony_ci * Bits[3:0] Read 0x9, LSB read 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int _idt82p33_settime(struct idt82p33_channel *channel, 2268c2ecf20Sopenharmony_ci struct timespec64 const *ts) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 2298c2ecf20Sopenharmony_ci struct timespec64 local_ts = *ts; 2308c2ecf20Sopenharmony_ci char buf[TOD_BYTE_COUNT]; 2318c2ecf20Sopenharmony_ci s64 dynamic_overhead_ns; 2328c2ecf20Sopenharmony_ci unsigned char trigger; 2338c2ecf20Sopenharmony_ci int err; 2348c2ecf20Sopenharmony_ci u8 i; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, 2378c2ecf20Sopenharmony_ci HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_tod_trigger, 2408c2ecf20Sopenharmony_ci &trigger, sizeof(trigger)); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (err) 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (idt82p33->calculate_overhead_flag) { 2468c2ecf20Sopenharmony_ci dynamic_overhead_ns = ktime_to_ns(ktime_get_raw()) 2478c2ecf20Sopenharmony_ci - ktime_to_ns(idt82p33->start_time); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci timespec64_add_ns(&local_ts, dynamic_overhead_ns); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci idt82p33->calculate_overhead_flag = 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci idt82p33_timespec_to_byte_array(&local_ts, buf); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * Store the new time value. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci for (i = 0; i < TOD_BYTE_COUNT; i++) { 2608c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_tod_cnfg + i, 2618c2ecf20Sopenharmony_ci &buf[i], sizeof(buf[i])); 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int _idt82p33_adjtime(struct idt82p33_channel *channel, s64 delta_ns) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 2728c2ecf20Sopenharmony_ci struct timespec64 ts; 2738c2ecf20Sopenharmony_ci s64 now_ns; 2748c2ecf20Sopenharmony_ci int err; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci idt82p33->calculate_overhead_flag = 1; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci err = _idt82p33_gettime(channel, &ts); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (err) 2818c2ecf20Sopenharmony_ci return err; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci now_ns = timespec64_to_ns(&ts); 2848c2ecf20Sopenharmony_ci now_ns += delta_ns + idt82p33->tod_write_overhead_ns; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ts = ns_to_timespec64(now_ns); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci err = _idt82p33_settime(channel, &ts); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 2968c2ecf20Sopenharmony_ci unsigned char buf[5] = {0}; 2978c2ecf20Sopenharmony_ci int neg_adj = 0; 2988c2ecf20Sopenharmony_ci int err, i; 2998c2ecf20Sopenharmony_ci s64 fcw; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (scaled_ppm == channel->current_freq_ppb) 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * Frequency Control Word unit is: 1.68 * 10^-10 ppm 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * adjfreq: 3088c2ecf20Sopenharmony_ci * ppb * 10^9 3098c2ecf20Sopenharmony_ci * FCW = ---------- 3108c2ecf20Sopenharmony_ci * 168 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * adjfine: 3138c2ecf20Sopenharmony_ci * scaled_ppm * 5^12 3148c2ecf20Sopenharmony_ci * FCW = ------------- 3158c2ecf20Sopenharmony_ci * 168 * 2^4 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 3188c2ecf20Sopenharmony_ci neg_adj = 1; 3198c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci fcw = scaled_ppm * 244140625ULL; 3238c2ecf20Sopenharmony_ci fcw = div_u64(fcw, 2688); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (neg_adj) 3268c2ecf20Sopenharmony_ci fcw = -fcw; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 3298c2ecf20Sopenharmony_ci buf[i] = fcw & 0xff; 3308c2ecf20Sopenharmony_ci fcw >>= 8; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci err = idt82p33_dpll_set_mode(channel, PLL_MODE_DCO); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (err) 3368c2ecf20Sopenharmony_ci return err; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_freq_cnfg, 3398c2ecf20Sopenharmony_ci buf, sizeof(buf)); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (err == 0) 3428c2ecf20Sopenharmony_ci channel->current_freq_ppb = scaled_ppm; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return err; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int idt82p33_measure_one_byte_write_overhead( 3488c2ecf20Sopenharmony_ci struct idt82p33_channel *channel, s64 *overhead_ns) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 3518c2ecf20Sopenharmony_ci ktime_t start, stop; 3528c2ecf20Sopenharmony_ci s64 total_ns; 3538c2ecf20Sopenharmony_ci u8 trigger; 3548c2ecf20Sopenharmony_ci int err; 3558c2ecf20Sopenharmony_ci u8 i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci total_ns = 0; 3588c2ecf20Sopenharmony_ci *overhead_ns = 0; 3598c2ecf20Sopenharmony_ci trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG, 3608c2ecf20Sopenharmony_ci HW_TOD_RD_TRIG_SEL_LSB_TOD_STS); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci for (i = 0; i < MAX_MEASURMENT_COUNT; i++) { 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci start = ktime_get_raw(); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_tod_trigger, 3678c2ecf20Sopenharmony_ci &trigger, sizeof(trigger)); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci stop = ktime_get_raw(); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (err) 3728c2ecf20Sopenharmony_ci return err; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci total_ns += ktime_to_ns(stop) - ktime_to_ns(start); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci *overhead_ns = div_s64(total_ns, MAX_MEASURMENT_COUNT); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int idt82p33_measure_tod_write_9_byte_overhead( 3838c2ecf20Sopenharmony_ci struct idt82p33_channel *channel) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 3868c2ecf20Sopenharmony_ci u8 buf[TOD_BYTE_COUNT]; 3878c2ecf20Sopenharmony_ci ktime_t start, stop; 3888c2ecf20Sopenharmony_ci s64 total_ns; 3898c2ecf20Sopenharmony_ci int err = 0; 3908c2ecf20Sopenharmony_ci u8 i, j; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci total_ns = 0; 3938c2ecf20Sopenharmony_ci idt82p33->tod_write_overhead_ns = 0; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci for (i = 0; i < MAX_MEASURMENT_COUNT; i++) { 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci start = ktime_get_raw(); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Need one less byte for applicable overhead */ 4008c2ecf20Sopenharmony_ci for (j = 0; j < (TOD_BYTE_COUNT - 1); j++) { 4018c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, 4028c2ecf20Sopenharmony_ci channel->dpll_tod_cnfg + i, 4038c2ecf20Sopenharmony_ci &buf[i], sizeof(buf[i])); 4048c2ecf20Sopenharmony_ci if (err) 4058c2ecf20Sopenharmony_ci return err; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci stop = ktime_get_raw(); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci total_ns += ktime_to_ns(stop) - ktime_to_ns(start); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci idt82p33->tod_write_overhead_ns = div_s64(total_ns, 4148c2ecf20Sopenharmony_ci MAX_MEASURMENT_COUNT); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return err; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int idt82p33_measure_settime_gettime_gap_overhead( 4208c2ecf20Sopenharmony_ci struct idt82p33_channel *channel, s64 *overhead_ns) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct timespec64 ts1 = {0, 0}; 4238c2ecf20Sopenharmony_ci struct timespec64 ts2; 4248c2ecf20Sopenharmony_ci int err; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci *overhead_ns = 0; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci err = _idt82p33_settime(channel, &ts1); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (err) 4318c2ecf20Sopenharmony_ci return err; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci err = _idt82p33_gettime(channel, &ts2); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!err) 4368c2ecf20Sopenharmony_ci *overhead_ns = timespec64_to_ns(&ts2) - timespec64_to_ns(&ts1); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return err; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci s64 trailing_overhead_ns, one_byte_write_ns, gap_ns; 4448c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 4458c2ecf20Sopenharmony_ci int err; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci idt82p33->tod_write_overhead_ns = 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci err = idt82p33_measure_settime_gettime_gap_overhead(channel, &gap_ns); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (err) 4528c2ecf20Sopenharmony_ci return err; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci err = idt82p33_measure_one_byte_write_overhead(channel, 4558c2ecf20Sopenharmony_ci &one_byte_write_ns); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (err) 4588c2ecf20Sopenharmony_ci return err; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci err = idt82p33_measure_tod_write_9_byte_overhead(channel); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (err) 4638c2ecf20Sopenharmony_ci return err; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci trailing_overhead_ns = gap_ns - (2 * one_byte_write_ns); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci idt82p33->tod_write_overhead_ns -= trailing_overhead_ns; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return err; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int idt82p33_check_and_set_masks(struct idt82p33 *idt82p33, 4738c2ecf20Sopenharmony_ci u8 page, 4748c2ecf20Sopenharmony_ci u8 offset, 4758c2ecf20Sopenharmony_ci u8 val) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int err = 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (page == PLLMASK_ADDR_HI && offset == PLLMASK_ADDR_LO) { 4808c2ecf20Sopenharmony_ci if ((val & 0xfc) || !(val & 0x3)) { 4818c2ecf20Sopenharmony_ci dev_err(&idt82p33->client->dev, 4828c2ecf20Sopenharmony_ci "Invalid PLL mask 0x%hhx\n", val); 4838c2ecf20Sopenharmony_ci err = -EINVAL; 4848c2ecf20Sopenharmony_ci } else { 4858c2ecf20Sopenharmony_ci idt82p33->pll_mask = val; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } else if (page == PLL0_OUTMASK_ADDR_HI && 4888c2ecf20Sopenharmony_ci offset == PLL0_OUTMASK_ADDR_LO) { 4898c2ecf20Sopenharmony_ci idt82p33->channel[0].output_mask = val; 4908c2ecf20Sopenharmony_ci } else if (page == PLL1_OUTMASK_ADDR_HI && 4918c2ecf20Sopenharmony_ci offset == PLL1_OUTMASK_ADDR_LO) { 4928c2ecf20Sopenharmony_ci idt82p33->channel[1].output_mask = val; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return err; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void idt82p33_display_masks(struct idt82p33 *idt82p33) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci u8 mask, i; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci dev_info(&idt82p33->client->dev, 5038c2ecf20Sopenharmony_ci "pllmask = 0x%02x\n", idt82p33->pll_mask); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PHC_PLL; i++) { 5068c2ecf20Sopenharmony_ci mask = 1 << i; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (mask & idt82p33->pll_mask) 5098c2ecf20Sopenharmony_ci dev_info(&idt82p33->client->dev, 5108c2ecf20Sopenharmony_ci "PLL%d output_mask = 0x%04x\n", 5118c2ecf20Sopenharmony_ci i, idt82p33->channel[i].output_mask); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 5188c2ecf20Sopenharmony_ci u8 sync_cnfg; 5198c2ecf20Sopenharmony_ci int err; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (enable == channel->sync_tod_on) { 5228c2ecf20Sopenharmony_ci if (enable && sync_tod_timeout) { 5238c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &channel->sync_tod_work, 5248c2ecf20Sopenharmony_ci sync_tod_timeout * HZ); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci err = idt82p33_read(idt82p33, channel->dpll_sync_cnfg, 5308c2ecf20Sopenharmony_ci &sync_cnfg, sizeof(sync_cnfg)); 5318c2ecf20Sopenharmony_ci if (err) 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci sync_cnfg &= ~SYNC_TOD; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (enable) 5378c2ecf20Sopenharmony_ci sync_cnfg |= SYNC_TOD; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_sync_cnfg, 5408c2ecf20Sopenharmony_ci &sync_cnfg, sizeof(sync_cnfg)); 5418c2ecf20Sopenharmony_ci if (err) 5428c2ecf20Sopenharmony_ci return err; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci channel->sync_tod_on = enable; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (enable && sync_tod_timeout) { 5478c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &channel->sync_tod_work, 5488c2ecf20Sopenharmony_ci sync_tod_timeout * HZ); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic void idt82p33_sync_tod_work_handler(struct work_struct *work) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 5578c2ecf20Sopenharmony_ci container_of(work, struct idt82p33_channel, sync_tod_work.work); 5588c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci (void)idt82p33_sync_tod(channel, false); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int idt82p33_pps_enable(struct idt82p33_channel *channel, bool enable) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 5708c2ecf20Sopenharmony_ci u8 mask, outn, val; 5718c2ecf20Sopenharmony_ci int err; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci mask = channel->output_mask; 5748c2ecf20Sopenharmony_ci outn = 0; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci while (mask) { 5778c2ecf20Sopenharmony_ci if (mask & 0x1) { 5788c2ecf20Sopenharmony_ci err = idt82p33_read(idt82p33, OUT_MUX_CNFG(outn), 5798c2ecf20Sopenharmony_ci &val, sizeof(val)); 5808c2ecf20Sopenharmony_ci if (err) 5818c2ecf20Sopenharmony_ci return err; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (enable) 5848c2ecf20Sopenharmony_ci val &= ~SQUELCH_ENABLE; 5858c2ecf20Sopenharmony_ci else 5868c2ecf20Sopenharmony_ci val |= SQUELCH_ENABLE; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, OUT_MUX_CNFG(outn), 5898c2ecf20Sopenharmony_ci &val, sizeof(val)); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (err) 5928c2ecf20Sopenharmony_ci return err; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci mask >>= 0x1; 5958c2ecf20Sopenharmony_ci outn++; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int idt82p33_enable_tod(struct idt82p33_channel *channel) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 6048c2ecf20Sopenharmony_ci struct timespec64 ts = {0, 0}; 6058c2ecf20Sopenharmony_ci int err; 6068c2ecf20Sopenharmony_ci u8 val; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci val = 0; 6098c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, channel->dpll_input_mode_cnfg, 6108c2ecf20Sopenharmony_ci &val, sizeof(val)); 6118c2ecf20Sopenharmony_ci if (err) 6128c2ecf20Sopenharmony_ci return err; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci err = idt82p33_pps_enable(channel, false); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (err) 6178c2ecf20Sopenharmony_ci return err; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci err = idt82p33_measure_tod_write_overhead(channel); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (err) 6228c2ecf20Sopenharmony_ci return err; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci err = _idt82p33_settime(channel, &ts); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (err) 6278c2ecf20Sopenharmony_ci return err; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return idt82p33_sync_tod(channel, true); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void idt82p33_ptp_clock_unregister_all(struct idt82p33 *idt82p33) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct idt82p33_channel *channel; 6358c2ecf20Sopenharmony_ci u8 i; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PHC_PLL; i++) { 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci channel = &idt82p33->channel[i]; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (channel->ptp_clock) { 6428c2ecf20Sopenharmony_ci ptp_clock_unregister(channel->ptp_clock); 6438c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&channel->sync_tod_work); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int idt82p33_enable(struct ptp_clock_info *ptp, 6498c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 6528c2ecf20Sopenharmony_ci container_of(ptp, struct idt82p33_channel, caps); 6538c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 6548c2ecf20Sopenharmony_ci int err; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (rq->type == PTP_CLK_REQ_PEROUT) { 6618c2ecf20Sopenharmony_ci if (!on) 6628c2ecf20Sopenharmony_ci err = idt82p33_pps_enable(channel, false); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* Only accept a 1-PPS aligned to the second. */ 6658c2ecf20Sopenharmony_ci else if (rq->perout.start.nsec || rq->perout.period.sec != 1 || 6668c2ecf20Sopenharmony_ci rq->perout.period.nsec) { 6678c2ecf20Sopenharmony_ci err = -ERANGE; 6688c2ecf20Sopenharmony_ci } else 6698c2ecf20Sopenharmony_ci err = idt82p33_pps_enable(channel, true); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return err; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 6808c2ecf20Sopenharmony_ci container_of(ptp, struct idt82p33_channel, caps); 6818c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 6828c2ecf20Sopenharmony_ci int err; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 6858c2ecf20Sopenharmony_ci err = _idt82p33_adjfine(channel, scaled_ppm); 6868c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return err; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic int idt82p33_adjtime(struct ptp_clock_info *ptp, s64 delta_ns) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 6948c2ecf20Sopenharmony_ci container_of(ptp, struct idt82p33_channel, caps); 6958c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 6968c2ecf20Sopenharmony_ci int err; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (abs(delta_ns) < phase_snap_threshold) { 7018c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci err = _idt82p33_adjtime(channel, delta_ns); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (err) { 7088c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 7098c2ecf20Sopenharmony_ci return err; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci err = idt82p33_sync_tod(channel, true); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return err; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int idt82p33_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 7228c2ecf20Sopenharmony_ci container_of(ptp, struct idt82p33_channel, caps); 7238c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 7248c2ecf20Sopenharmony_ci int err; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 7278c2ecf20Sopenharmony_ci err = _idt82p33_gettime(channel, ts); 7288c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int idt82p33_settime(struct ptp_clock_info *ptp, 7348c2ecf20Sopenharmony_ci const struct timespec64 *ts) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct idt82p33_channel *channel = 7378c2ecf20Sopenharmony_ci container_of(ptp, struct idt82p33_channel, caps); 7388c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = channel->idt82p33; 7398c2ecf20Sopenharmony_ci int err; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 7428c2ecf20Sopenharmony_ci err = _idt82p33_settime(channel, ts); 7438c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return err; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int idt82p33_channel_init(struct idt82p33_channel *channel, int index) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci switch (index) { 7518c2ecf20Sopenharmony_ci case 0: 7528c2ecf20Sopenharmony_ci channel->dpll_tod_cnfg = DPLL1_TOD_CNFG; 7538c2ecf20Sopenharmony_ci channel->dpll_tod_trigger = DPLL1_TOD_TRIGGER; 7548c2ecf20Sopenharmony_ci channel->dpll_tod_sts = DPLL1_TOD_STS; 7558c2ecf20Sopenharmony_ci channel->dpll_mode_cnfg = DPLL1_OPERATING_MODE_CNFG; 7568c2ecf20Sopenharmony_ci channel->dpll_freq_cnfg = DPLL1_HOLDOVER_FREQ_CNFG; 7578c2ecf20Sopenharmony_ci channel->dpll_phase_cnfg = DPLL1_PHASE_OFFSET_CNFG; 7588c2ecf20Sopenharmony_ci channel->dpll_sync_cnfg = DPLL1_SYNC_EDGE_CNFG; 7598c2ecf20Sopenharmony_ci channel->dpll_input_mode_cnfg = DPLL1_INPUT_MODE_CNFG; 7608c2ecf20Sopenharmony_ci break; 7618c2ecf20Sopenharmony_ci case 1: 7628c2ecf20Sopenharmony_ci channel->dpll_tod_cnfg = DPLL2_TOD_CNFG; 7638c2ecf20Sopenharmony_ci channel->dpll_tod_trigger = DPLL2_TOD_TRIGGER; 7648c2ecf20Sopenharmony_ci channel->dpll_tod_sts = DPLL2_TOD_STS; 7658c2ecf20Sopenharmony_ci channel->dpll_mode_cnfg = DPLL2_OPERATING_MODE_CNFG; 7668c2ecf20Sopenharmony_ci channel->dpll_freq_cnfg = DPLL2_HOLDOVER_FREQ_CNFG; 7678c2ecf20Sopenharmony_ci channel->dpll_phase_cnfg = DPLL2_PHASE_OFFSET_CNFG; 7688c2ecf20Sopenharmony_ci channel->dpll_sync_cnfg = DPLL2_SYNC_EDGE_CNFG; 7698c2ecf20Sopenharmony_ci channel->dpll_input_mode_cnfg = DPLL2_INPUT_MODE_CNFG; 7708c2ecf20Sopenharmony_ci break; 7718c2ecf20Sopenharmony_ci default: 7728c2ecf20Sopenharmony_ci return -EINVAL; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&channel->sync_tod_work, 7768c2ecf20Sopenharmony_ci idt82p33_sync_tod_work_handler); 7778c2ecf20Sopenharmony_ci channel->sync_tod_on = false; 7788c2ecf20Sopenharmony_ci channel->current_freq_ppb = 0; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci return 0; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic void idt82p33_caps_init(struct ptp_clock_info *caps) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci caps->owner = THIS_MODULE; 7868c2ecf20Sopenharmony_ci caps->max_adj = 92000; 7878c2ecf20Sopenharmony_ci caps->adjfine = idt82p33_adjfine; 7888c2ecf20Sopenharmony_ci caps->adjtime = idt82p33_adjtime; 7898c2ecf20Sopenharmony_ci caps->gettime64 = idt82p33_gettime; 7908c2ecf20Sopenharmony_ci caps->settime64 = idt82p33_settime; 7918c2ecf20Sopenharmony_ci caps->enable = idt82p33_enable; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct idt82p33_channel *channel; 7978c2ecf20Sopenharmony_ci int err; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (!(index < MAX_PHC_PLL)) 8008c2ecf20Sopenharmony_ci return -EINVAL; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci channel = &idt82p33->channel[index]; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci err = idt82p33_channel_init(channel, index); 8058c2ecf20Sopenharmony_ci if (err) 8068c2ecf20Sopenharmony_ci return err; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci channel->idt82p33 = idt82p33; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci idt82p33_caps_init(&channel->caps); 8118c2ecf20Sopenharmony_ci snprintf(channel->caps.name, sizeof(channel->caps.name), 8128c2ecf20Sopenharmony_ci "IDT 82P33 PLL%u", index); 8138c2ecf20Sopenharmony_ci channel->caps.n_per_out = hweight8(channel->output_mask); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci err = idt82p33_dpll_set_mode(channel, PLL_MODE_DCO); 8168c2ecf20Sopenharmony_ci if (err) 8178c2ecf20Sopenharmony_ci return err; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci err = idt82p33_enable_tod(channel); 8208c2ecf20Sopenharmony_ci if (err) 8218c2ecf20Sopenharmony_ci return err; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (IS_ERR(channel->ptp_clock)) { 8268c2ecf20Sopenharmony_ci err = PTR_ERR(channel->ptp_clock); 8278c2ecf20Sopenharmony_ci channel->ptp_clock = NULL; 8288c2ecf20Sopenharmony_ci return err; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (!channel->ptp_clock) 8328c2ecf20Sopenharmony_ci return -ENOTSUPP; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci dev_info(&idt82p33->client->dev, "PLL%d registered as ptp%d\n", 8358c2ecf20Sopenharmony_ci index, channel->ptp_clock->index); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return 0; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic int idt82p33_load_firmware(struct idt82p33 *idt82p33) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci const struct firmware *fw; 8438c2ecf20Sopenharmony_ci struct idt82p33_fwrc *rec; 8448c2ecf20Sopenharmony_ci u8 loaddr, page, val; 8458c2ecf20Sopenharmony_ci int err; 8468c2ecf20Sopenharmony_ci s32 len; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci dev_dbg(&idt82p33->client->dev, 8498c2ecf20Sopenharmony_ci "requesting firmware '%s'\n", FW_FILENAME); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci err = request_firmware(&fw, FW_FILENAME, &idt82p33->client->dev); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (err) 8548c2ecf20Sopenharmony_ci return err; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci dev_dbg(&idt82p33->client->dev, "firmware size %zu bytes\n", fw->size); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci rec = (struct idt82p33_fwrc *) fw->data; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci for (len = fw->size; len > 0; len -= sizeof(*rec)) { 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (rec->reserved) { 8638c2ecf20Sopenharmony_ci dev_err(&idt82p33->client->dev, 8648c2ecf20Sopenharmony_ci "bad firmware, reserved field non-zero\n"); 8658c2ecf20Sopenharmony_ci err = -EINVAL; 8668c2ecf20Sopenharmony_ci } else { 8678c2ecf20Sopenharmony_ci val = rec->value; 8688c2ecf20Sopenharmony_ci loaddr = rec->loaddr; 8698c2ecf20Sopenharmony_ci page = rec->hiaddr; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci rec++; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci err = idt82p33_check_and_set_masks(idt82p33, page, 8748c2ecf20Sopenharmony_ci loaddr, val); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (err == 0) { 8788c2ecf20Sopenharmony_ci /* maximum 8 pages */ 8798c2ecf20Sopenharmony_ci if (page >= PAGE_NUM) 8808c2ecf20Sopenharmony_ci continue; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* Page size 128, last 4 bytes of page skipped */ 8838c2ecf20Sopenharmony_ci if (((loaddr > 0x7b) && (loaddr <= 0x7f)) 8848c2ecf20Sopenharmony_ci || loaddr > 0xfb) 8858c2ecf20Sopenharmony_ci continue; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci err = idt82p33_write(idt82p33, _ADDR(page, loaddr), 8888c2ecf20Sopenharmony_ci &val, sizeof(val)); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (err) 8928c2ecf20Sopenharmony_ci goto out; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci idt82p33_display_masks(idt82p33); 8968c2ecf20Sopenharmony_ciout: 8978c2ecf20Sopenharmony_ci release_firmware(fw); 8988c2ecf20Sopenharmony_ci return err; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int idt82p33_probe(struct i2c_client *client, 9038c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33; 9068c2ecf20Sopenharmony_ci int err; 9078c2ecf20Sopenharmony_ci u8 i; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci (void)id; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci idt82p33 = devm_kzalloc(&client->dev, 9128c2ecf20Sopenharmony_ci sizeof(struct idt82p33), GFP_KERNEL); 9138c2ecf20Sopenharmony_ci if (!idt82p33) 9148c2ecf20Sopenharmony_ci return -ENOMEM; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci mutex_init(&idt82p33->reg_lock); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci idt82p33->client = client; 9198c2ecf20Sopenharmony_ci idt82p33->page_offset = 0xff; 9208c2ecf20Sopenharmony_ci idt82p33->tod_write_overhead_ns = 0; 9218c2ecf20Sopenharmony_ci idt82p33->calculate_overhead_flag = 0; 9228c2ecf20Sopenharmony_ci idt82p33->pll_mask = DEFAULT_PLL_MASK; 9238c2ecf20Sopenharmony_ci idt82p33->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; 9248c2ecf20Sopenharmony_ci idt82p33->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci mutex_lock(&idt82p33->reg_lock); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci err = idt82p33_load_firmware(idt82p33); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (err) 9318c2ecf20Sopenharmony_ci dev_warn(&idt82p33->client->dev, 9328c2ecf20Sopenharmony_ci "loading firmware failed with %d\n", err); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (idt82p33->pll_mask) { 9358c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PHC_PLL; i++) { 9368c2ecf20Sopenharmony_ci if (idt82p33->pll_mask & (1 << i)) { 9378c2ecf20Sopenharmony_ci err = idt82p33_enable_channel(idt82p33, i); 9388c2ecf20Sopenharmony_ci if (err) 9398c2ecf20Sopenharmony_ci break; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci } else { 9438c2ecf20Sopenharmony_ci dev_err(&idt82p33->client->dev, 9448c2ecf20Sopenharmony_ci "no PLLs flagged as PHCs, nothing to do\n"); 9458c2ecf20Sopenharmony_ci err = -ENODEV; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci mutex_unlock(&idt82p33->reg_lock); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (err) { 9518c2ecf20Sopenharmony_ci idt82p33_ptp_clock_unregister_all(idt82p33); 9528c2ecf20Sopenharmony_ci return err; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci i2c_set_clientdata(client, idt82p33); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci return 0; 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int idt82p33_remove(struct i2c_client *client) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct idt82p33 *idt82p33 = i2c_get_clientdata(client); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci idt82p33_ptp_clock_unregister_all(idt82p33); 9658c2ecf20Sopenharmony_ci mutex_destroy(&idt82p33->reg_lock); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return 0; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 9718c2ecf20Sopenharmony_cistatic const struct of_device_id idt82p33_dt_id[] = { 9728c2ecf20Sopenharmony_ci { .compatible = "idt,82p33810" }, 9738c2ecf20Sopenharmony_ci { .compatible = "idt,82p33813" }, 9748c2ecf20Sopenharmony_ci { .compatible = "idt,82p33814" }, 9758c2ecf20Sopenharmony_ci { .compatible = "idt,82p33831" }, 9768c2ecf20Sopenharmony_ci { .compatible = "idt,82p33910" }, 9778c2ecf20Sopenharmony_ci { .compatible = "idt,82p33913" }, 9788c2ecf20Sopenharmony_ci { .compatible = "idt,82p33914" }, 9798c2ecf20Sopenharmony_ci { .compatible = "idt,82p33931" }, 9808c2ecf20Sopenharmony_ci {}, 9818c2ecf20Sopenharmony_ci}; 9828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, idt82p33_dt_id); 9838c2ecf20Sopenharmony_ci#endif 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic const struct i2c_device_id idt82p33_i2c_id[] = { 9868c2ecf20Sopenharmony_ci { "idt82p33810", }, 9878c2ecf20Sopenharmony_ci { "idt82p33813", }, 9888c2ecf20Sopenharmony_ci { "idt82p33814", }, 9898c2ecf20Sopenharmony_ci { "idt82p33831", }, 9908c2ecf20Sopenharmony_ci { "idt82p33910", }, 9918c2ecf20Sopenharmony_ci { "idt82p33913", }, 9928c2ecf20Sopenharmony_ci { "idt82p33914", }, 9938c2ecf20Sopenharmony_ci { "idt82p33931", }, 9948c2ecf20Sopenharmony_ci {}, 9958c2ecf20Sopenharmony_ci}; 9968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, idt82p33_i2c_id); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic struct i2c_driver idt82p33_driver = { 9998c2ecf20Sopenharmony_ci .driver = { 10008c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(idt82p33_dt_id), 10018c2ecf20Sopenharmony_ci .name = "idt82p33", 10028c2ecf20Sopenharmony_ci }, 10038c2ecf20Sopenharmony_ci .probe = idt82p33_probe, 10048c2ecf20Sopenharmony_ci .remove = idt82p33_remove, 10058c2ecf20Sopenharmony_ci .id_table = idt82p33_i2c_id, 10068c2ecf20Sopenharmony_ci}; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cimodule_i2c_driver(idt82p33_driver); 1009