13a6ba7dcSVincent Cheng // SPDX-License-Identifier: GPL-2.0+ 23a6ba7dcSVincent Cheng /* 33a6ba7dcSVincent Cheng * PTP hardware clock driver for the IDT ClockMatrix(TM) family of timing and 43a6ba7dcSVincent Cheng * synchronization devices. 53a6ba7dcSVincent Cheng * 63a6ba7dcSVincent Cheng * Copyright (C) 2019 Integrated Device Technology, Inc., a Renesas Company. 73a6ba7dcSVincent Cheng */ 83a6ba7dcSVincent Cheng #include <linux/firmware.h> 93a6ba7dcSVincent Cheng #include <linux/i2c.h> 103a6ba7dcSVincent Cheng #include <linux/module.h> 113a6ba7dcSVincent Cheng #include <linux/ptp_clock_kernel.h> 123a6ba7dcSVincent Cheng #include <linux/delay.h> 13425d2b1cSVincent Cheng #include <linux/jiffies.h> 143a6ba7dcSVincent Cheng #include <linux/kernel.h> 153a6ba7dcSVincent Cheng #include <linux/timekeeping.h> 167ea5fda2SMin Li #include <linux/string.h> 173a6ba7dcSVincent Cheng 183a6ba7dcSVincent Cheng #include "ptp_private.h" 193a6ba7dcSVincent Cheng #include "ptp_clockmatrix.h" 203a6ba7dcSVincent Cheng 213a6ba7dcSVincent Cheng MODULE_DESCRIPTION("Driver for IDT ClockMatrix(TM) family"); 223a6ba7dcSVincent Cheng MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 233a6ba7dcSVincent Cheng MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>"); 243a6ba7dcSVincent Cheng MODULE_VERSION("1.0"); 253a6ba7dcSVincent Cheng MODULE_LICENSE("GPL"); 263a6ba7dcSVincent Cheng 277ea5fda2SMin Li /* 287ea5fda2SMin Li * The name of the firmware file to be loaded 297ea5fda2SMin Li * over-rides any automatic selection 307ea5fda2SMin Li */ 317ea5fda2SMin Li static char *firmware; 327ea5fda2SMin Li module_param(firmware, charp, 0); 337ea5fda2SMin Li 343a6ba7dcSVincent Cheng #define SETTIME_CORRECTION (0) 353a6ba7dcSVincent Cheng 36251f4fe2SMin Li static int contains_full_configuration(const struct firmware *fw) 37251f4fe2SMin Li { 38251f4fe2SMin Li s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES; 39251f4fe2SMin Li struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data; 40251f4fe2SMin Li s32 count = 0; 41251f4fe2SMin Li u16 regaddr; 42251f4fe2SMin Li u8 loaddr; 43251f4fe2SMin Li s32 len; 44251f4fe2SMin Li 45251f4fe2SMin Li /* If the firmware contains 'full configuration' SM_RESET can be used 46251f4fe2SMin Li * to ensure proper configuration. 47251f4fe2SMin Li * 48251f4fe2SMin Li * Full configuration is defined as the number of programmable 49251f4fe2SMin Li * bytes within the configuration range minus page offset addr range. 50251f4fe2SMin Li */ 51251f4fe2SMin Li for (len = fw->size; len > 0; len -= sizeof(*rec)) { 52251f4fe2SMin Li regaddr = rec->hiaddr << 8; 53251f4fe2SMin Li regaddr |= rec->loaddr; 54251f4fe2SMin Li 55251f4fe2SMin Li loaddr = rec->loaddr; 56251f4fe2SMin Li 57251f4fe2SMin Li rec++; 58251f4fe2SMin Li 59251f4fe2SMin Li /* Top (status registers) and bottom are read-only */ 60251f4fe2SMin Li if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH) 61251f4fe2SMin Li continue; 62251f4fe2SMin Li 63251f4fe2SMin Li /* Page size 128, last 4 bytes of page skipped */ 64251f4fe2SMin Li if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb) 65251f4fe2SMin Li continue; 66251f4fe2SMin Li 67251f4fe2SMin Li count++; 68251f4fe2SMin Li } 69251f4fe2SMin Li 70251f4fe2SMin Li return (count >= full_count); 71251f4fe2SMin Li } 72251f4fe2SMin Li 733a6ba7dcSVincent Cheng static int char_array_to_timespec(u8 *buf, 743a6ba7dcSVincent Cheng u8 count, 753a6ba7dcSVincent Cheng struct timespec64 *ts) 763a6ba7dcSVincent Cheng { 773a6ba7dcSVincent Cheng u8 i; 783a6ba7dcSVincent Cheng u64 nsec; 793a6ba7dcSVincent Cheng time64_t sec; 803a6ba7dcSVincent Cheng 813a6ba7dcSVincent Cheng if (count < TOD_BYTE_COUNT) 823a6ba7dcSVincent Cheng return 1; 833a6ba7dcSVincent Cheng 843a6ba7dcSVincent Cheng /* Sub-nanoseconds are in buf[0]. */ 853a6ba7dcSVincent Cheng nsec = buf[4]; 863a6ba7dcSVincent Cheng for (i = 0; i < 3; i++) { 873a6ba7dcSVincent Cheng nsec <<= 8; 883a6ba7dcSVincent Cheng nsec |= buf[3 - i]; 893a6ba7dcSVincent Cheng } 903a6ba7dcSVincent Cheng 913a6ba7dcSVincent Cheng sec = buf[10]; 923a6ba7dcSVincent Cheng for (i = 0; i < 5; i++) { 933a6ba7dcSVincent Cheng sec <<= 8; 943a6ba7dcSVincent Cheng sec |= buf[9 - i]; 953a6ba7dcSVincent Cheng } 963a6ba7dcSVincent Cheng 973a6ba7dcSVincent Cheng ts->tv_sec = sec; 983a6ba7dcSVincent Cheng ts->tv_nsec = nsec; 993a6ba7dcSVincent Cheng 1003a6ba7dcSVincent Cheng return 0; 1013a6ba7dcSVincent Cheng } 1023a6ba7dcSVincent Cheng 1033a6ba7dcSVincent Cheng static int timespec_to_char_array(struct timespec64 const *ts, 1043a6ba7dcSVincent Cheng u8 *buf, 1053a6ba7dcSVincent Cheng u8 count) 1063a6ba7dcSVincent Cheng { 1073a6ba7dcSVincent Cheng u8 i; 1083a6ba7dcSVincent Cheng s32 nsec; 1093a6ba7dcSVincent Cheng time64_t sec; 1103a6ba7dcSVincent Cheng 1113a6ba7dcSVincent Cheng if (count < TOD_BYTE_COUNT) 1123a6ba7dcSVincent Cheng return 1; 1133a6ba7dcSVincent Cheng 1143a6ba7dcSVincent Cheng nsec = ts->tv_nsec; 1153a6ba7dcSVincent Cheng sec = ts->tv_sec; 1163a6ba7dcSVincent Cheng 1173a6ba7dcSVincent Cheng /* Sub-nanoseconds are in buf[0]. */ 1183a6ba7dcSVincent Cheng buf[0] = 0; 1193a6ba7dcSVincent Cheng for (i = 1; i < 5; i++) { 1203a6ba7dcSVincent Cheng buf[i] = nsec & 0xff; 1213a6ba7dcSVincent Cheng nsec >>= 8; 1223a6ba7dcSVincent Cheng } 1233a6ba7dcSVincent Cheng 1243a6ba7dcSVincent Cheng for (i = 5; i < TOD_BYTE_COUNT; i++) { 1253a6ba7dcSVincent Cheng 1263a6ba7dcSVincent Cheng buf[i] = sec & 0xff; 1273a6ba7dcSVincent Cheng sec >>= 8; 1283a6ba7dcSVincent Cheng } 1293a6ba7dcSVincent Cheng 1303a6ba7dcSVincent Cheng return 0; 1313a6ba7dcSVincent Cheng } 1323a6ba7dcSVincent Cheng 1333cb2e6d9SMin Li static int idtcm_strverscmp(const char *version1, const char *version2) 1347ea5fda2SMin Li { 1353cb2e6d9SMin Li u8 ver1[3], ver2[3]; 1363cb2e6d9SMin Li int i; 1377ea5fda2SMin Li 1383cb2e6d9SMin Li if (sscanf(version1, "%hhu.%hhu.%hhu", 1393cb2e6d9SMin Li &ver1[0], &ver1[1], &ver1[2]) != 3) 1403cb2e6d9SMin Li return -1; 1413cb2e6d9SMin Li if (sscanf(version2, "%hhu.%hhu.%hhu", 1423cb2e6d9SMin Li &ver2[0], &ver2[1], &ver2[2]) != 3) 1437ea5fda2SMin Li return -1; 1447ea5fda2SMin Li 1453cb2e6d9SMin Li for (i = 0; i < 3; i++) { 1463cb2e6d9SMin Li if (ver1[i] > ver2[i]) 1473cb2e6d9SMin Li return 1; 1483cb2e6d9SMin Li if (ver1[i] < ver2[i]) 1497ea5fda2SMin Li return -1; 1503cb2e6d9SMin Li } 1517ea5fda2SMin Li 1523cb2e6d9SMin Li return 0; 1537ea5fda2SMin Li } 1547ea5fda2SMin Li 155957ff427SMin Li static int idtcm_xfer_read(struct idtcm *idtcm, 1563a6ba7dcSVincent Cheng u8 regaddr, 1573a6ba7dcSVincent Cheng u8 *buf, 158957ff427SMin Li u16 count) 1593a6ba7dcSVincent Cheng { 1603a6ba7dcSVincent Cheng struct i2c_client *client = idtcm->client; 1613a6ba7dcSVincent Cheng struct i2c_msg msg[2]; 1623a6ba7dcSVincent Cheng int cnt; 163957ff427SMin Li char *fmt = "i2c_transfer failed at %d in %s, at addr: %04X!\n"; 1643a6ba7dcSVincent Cheng 1653a6ba7dcSVincent Cheng msg[0].addr = client->addr; 1663a6ba7dcSVincent Cheng msg[0].flags = 0; 1673a6ba7dcSVincent Cheng msg[0].len = 1; 1683a6ba7dcSVincent Cheng msg[0].buf = ®addr; 1693a6ba7dcSVincent Cheng 1703a6ba7dcSVincent Cheng msg[1].addr = client->addr; 171957ff427SMin Li msg[1].flags = I2C_M_RD; 1723a6ba7dcSVincent Cheng msg[1].len = count; 1733a6ba7dcSVincent Cheng msg[1].buf = buf; 1743a6ba7dcSVincent Cheng 1753a6ba7dcSVincent Cheng cnt = i2c_transfer(client->adapter, msg, 2); 1763a6ba7dcSVincent Cheng 1773a6ba7dcSVincent Cheng if (cnt < 0) { 1787ea5fda2SMin Li dev_err(&client->dev, 1797ea5fda2SMin Li fmt, 1807ea5fda2SMin Li __LINE__, 1817ea5fda2SMin Li __func__, 1827ea5fda2SMin Li regaddr); 1833a6ba7dcSVincent Cheng return cnt; 1843a6ba7dcSVincent Cheng } else if (cnt != 2) { 1853a6ba7dcSVincent Cheng dev_err(&client->dev, 1863a6ba7dcSVincent Cheng "i2c_transfer sent only %d of %d messages\n", cnt, 2); 1873a6ba7dcSVincent Cheng return -EIO; 1883a6ba7dcSVincent Cheng } 1893a6ba7dcSVincent Cheng 1903a6ba7dcSVincent Cheng return 0; 1913a6ba7dcSVincent Cheng } 1923a6ba7dcSVincent Cheng 193957ff427SMin Li static int idtcm_xfer_write(struct idtcm *idtcm, 194957ff427SMin Li u8 regaddr, 195957ff427SMin Li u8 *buf, 196957ff427SMin Li u16 count) 197957ff427SMin Li { 198957ff427SMin Li struct i2c_client *client = idtcm->client; 199957ff427SMin Li /* we add 1 byte for device register */ 200957ff427SMin Li u8 msg[IDTCM_MAX_WRITE_COUNT + 1]; 201957ff427SMin Li int cnt; 202957ff427SMin Li char *fmt = "i2c_master_send failed at %d in %s, at addr: %04X!\n"; 203957ff427SMin Li 204957ff427SMin Li if (count > IDTCM_MAX_WRITE_COUNT) 205957ff427SMin Li return -EINVAL; 206957ff427SMin Li 207957ff427SMin Li msg[0] = regaddr; 208957ff427SMin Li memcpy(&msg[1], buf, count); 209957ff427SMin Li 210957ff427SMin Li cnt = i2c_master_send(client, msg, count + 1); 211957ff427SMin Li 212957ff427SMin Li if (cnt < 0) { 213957ff427SMin Li dev_err(&client->dev, 214957ff427SMin Li fmt, 215957ff427SMin Li __LINE__, 216957ff427SMin Li __func__, 217957ff427SMin Li regaddr); 218957ff427SMin Li return cnt; 219957ff427SMin Li } 220957ff427SMin Li 221957ff427SMin Li return 0; 222957ff427SMin Li } 223957ff427SMin Li 2243a6ba7dcSVincent Cheng static int idtcm_page_offset(struct idtcm *idtcm, u8 val) 2253a6ba7dcSVincent Cheng { 2263a6ba7dcSVincent Cheng u8 buf[4]; 2273a6ba7dcSVincent Cheng int err; 2283a6ba7dcSVincent Cheng 2293a6ba7dcSVincent Cheng if (idtcm->page_offset == val) 2303a6ba7dcSVincent Cheng return 0; 2313a6ba7dcSVincent Cheng 2323a6ba7dcSVincent Cheng buf[0] = 0x0; 2333a6ba7dcSVincent Cheng buf[1] = val; 2343a6ba7dcSVincent Cheng buf[2] = 0x10; 2353a6ba7dcSVincent Cheng buf[3] = 0x20; 2363a6ba7dcSVincent Cheng 237957ff427SMin Li err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf)); 2383a6ba7dcSVincent Cheng 2397ea5fda2SMin Li if (err) { 2407ea5fda2SMin Li idtcm->page_offset = 0xff; 2413a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, "failed to set page offset\n"); 2427ea5fda2SMin Li } else { 2433a6ba7dcSVincent Cheng idtcm->page_offset = val; 2447ea5fda2SMin Li } 2453a6ba7dcSVincent Cheng 2463a6ba7dcSVincent Cheng return err; 2473a6ba7dcSVincent Cheng } 2483a6ba7dcSVincent Cheng 2493a6ba7dcSVincent Cheng static int _idtcm_rdwr(struct idtcm *idtcm, 2503a6ba7dcSVincent Cheng u16 regaddr, 2513a6ba7dcSVincent Cheng u8 *buf, 2523a6ba7dcSVincent Cheng u16 count, 2533a6ba7dcSVincent Cheng bool write) 2543a6ba7dcSVincent Cheng { 2553a6ba7dcSVincent Cheng u8 hi; 2563a6ba7dcSVincent Cheng u8 lo; 2573a6ba7dcSVincent Cheng int err; 2583a6ba7dcSVincent Cheng 2593a6ba7dcSVincent Cheng hi = (regaddr >> 8) & 0xff; 2603a6ba7dcSVincent Cheng lo = regaddr & 0xff; 2613a6ba7dcSVincent Cheng 2623a6ba7dcSVincent Cheng err = idtcm_page_offset(idtcm, hi); 2633a6ba7dcSVincent Cheng 2643a6ba7dcSVincent Cheng if (err) 2653a6ba7dcSVincent Cheng return err; 266957ff427SMin Li 267957ff427SMin Li if (write) 268957ff427SMin Li return idtcm_xfer_write(idtcm, lo, buf, count); 269957ff427SMin Li 270957ff427SMin Li return idtcm_xfer_read(idtcm, lo, buf, count); 2713a6ba7dcSVincent Cheng } 2723a6ba7dcSVincent Cheng 2733a6ba7dcSVincent Cheng static int idtcm_read(struct idtcm *idtcm, 2743a6ba7dcSVincent Cheng u16 module, 2753a6ba7dcSVincent Cheng u16 regaddr, 2763a6ba7dcSVincent Cheng u8 *buf, 2773a6ba7dcSVincent Cheng u16 count) 2783a6ba7dcSVincent Cheng { 2793a6ba7dcSVincent Cheng return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false); 2803a6ba7dcSVincent Cheng } 2813a6ba7dcSVincent Cheng 2823a6ba7dcSVincent Cheng static int idtcm_write(struct idtcm *idtcm, 2833a6ba7dcSVincent Cheng u16 module, 2843a6ba7dcSVincent Cheng u16 regaddr, 2853a6ba7dcSVincent Cheng u8 *buf, 2863a6ba7dcSVincent Cheng u16 count) 2873a6ba7dcSVincent Cheng { 2883a6ba7dcSVincent Cheng return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true); 2893a6ba7dcSVincent Cheng } 2903a6ba7dcSVincent Cheng 291251f4fe2SMin Li static int clear_boot_status(struct idtcm *idtcm) 292251f4fe2SMin Li { 293251f4fe2SMin Li int err; 294251f4fe2SMin Li u8 buf[4] = {0}; 295251f4fe2SMin Li 296251f4fe2SMin Li err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 297251f4fe2SMin Li 298251f4fe2SMin Li return err; 299251f4fe2SMin Li } 300251f4fe2SMin Li 301251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status) 302251f4fe2SMin Li { 303251f4fe2SMin Li int err; 304251f4fe2SMin Li u8 buf[4] = {0}; 305251f4fe2SMin Li 306251f4fe2SMin Li err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 307251f4fe2SMin Li 308251f4fe2SMin Li *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; 309251f4fe2SMin Li 310251f4fe2SMin Li return err; 311251f4fe2SMin Li } 312251f4fe2SMin Li 313251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm) 314251f4fe2SMin Li { 315251f4fe2SMin Li u32 status = 0; 316251f4fe2SMin Li u8 i = 30; /* 30 * 100ms = 3s */ 317251f4fe2SMin Li int err; 318251f4fe2SMin Li 319251f4fe2SMin Li do { 320251f4fe2SMin Li err = read_boot_status(idtcm, &status); 321251f4fe2SMin Li 322251f4fe2SMin Li if (err) 323251f4fe2SMin Li return err; 324251f4fe2SMin Li 325251f4fe2SMin Li if (status == 0xA0) 326251f4fe2SMin Li return 0; 327251f4fe2SMin Li 328251f4fe2SMin Li msleep(100); 329251f4fe2SMin Li i--; 330251f4fe2SMin Li 331251f4fe2SMin Li } while (i); 332251f4fe2SMin Li 333251f4fe2SMin Li dev_warn(&idtcm->client->dev, "%s timed out\n", __func__); 334251f4fe2SMin Li 335251f4fe2SMin Li return -EBUSY; 336251f4fe2SMin Li } 337251f4fe2SMin Li 3383a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel, 3393a6ba7dcSVincent Cheng struct timespec64 *ts) 3403a6ba7dcSVincent Cheng { 3413a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 3423a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 3437ea5fda2SMin Li u8 timeout = 10; 3443a6ba7dcSVincent Cheng u8 trigger; 3453a6ba7dcSVincent Cheng int err; 3463a6ba7dcSVincent Cheng 3473a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 3483a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 3493a6ba7dcSVincent Cheng if (err) 3503a6ba7dcSVincent Cheng return err; 3513a6ba7dcSVincent Cheng 3523a6ba7dcSVincent Cheng trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT); 3533a6ba7dcSVincent Cheng trigger |= (1 << TOD_READ_TRIGGER_SHIFT); 3547ea5fda2SMin Li trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */ 3553a6ba7dcSVincent Cheng 3563a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_read_primary, 3573a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 3587ea5fda2SMin Li if (err) 3597ea5fda2SMin Li return err; 3607ea5fda2SMin Li 3617ea5fda2SMin Li /* wait trigger to be 0 */ 3627ea5fda2SMin Li while (trigger & TOD_READ_TRIGGER_MASK) { 3637ea5fda2SMin Li 3647ea5fda2SMin Li if (idtcm->calculate_overhead_flag) 3657ea5fda2SMin Li idtcm->start_time = ktime_get_raw(); 3667ea5fda2SMin Li 3677ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_read_primary, 3687ea5fda2SMin Li TOD_READ_PRIMARY_CMD, &trigger, 3697ea5fda2SMin Li sizeof(trigger)); 3703a6ba7dcSVincent Cheng 3713a6ba7dcSVincent Cheng if (err) 3723a6ba7dcSVincent Cheng return err; 3733a6ba7dcSVincent Cheng 3747ea5fda2SMin Li if (--timeout == 0) 3757ea5fda2SMin Li return -EIO; 3767ea5fda2SMin Li } 3773a6ba7dcSVincent Cheng 3783a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 3793a6ba7dcSVincent Cheng TOD_READ_PRIMARY, buf, sizeof(buf)); 3803a6ba7dcSVincent Cheng 3813a6ba7dcSVincent Cheng if (err) 3823a6ba7dcSVincent Cheng return err; 3833a6ba7dcSVincent Cheng 3843a6ba7dcSVincent Cheng err = char_array_to_timespec(buf, sizeof(buf), ts); 3853a6ba7dcSVincent Cheng 3863a6ba7dcSVincent Cheng return err; 3873a6ba7dcSVincent Cheng } 3883a6ba7dcSVincent Cheng 3893a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm, 3903a6ba7dcSVincent Cheng u8 pll, 3913a6ba7dcSVincent Cheng u8 sync_src, 3923a6ba7dcSVincent Cheng u8 qn, 3933a6ba7dcSVincent Cheng u8 qn_plus_1) 3943a6ba7dcSVincent Cheng { 3953a6ba7dcSVincent Cheng int err; 3963a6ba7dcSVincent Cheng u8 val; 3973a6ba7dcSVincent Cheng u16 sync_ctrl0; 3983a6ba7dcSVincent Cheng u16 sync_ctrl1; 3997ea5fda2SMin Li u8 temp; 4003a6ba7dcSVincent Cheng 4013a6ba7dcSVincent Cheng if ((qn == 0) && (qn_plus_1 == 0)) 4023a6ba7dcSVincent Cheng return 0; 4033a6ba7dcSVincent Cheng 4043a6ba7dcSVincent Cheng switch (pll) { 4053a6ba7dcSVincent Cheng case 0: 4063a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0; 4073a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1; 4083a6ba7dcSVincent Cheng break; 4093a6ba7dcSVincent Cheng case 1: 4103a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0; 4113a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1; 4123a6ba7dcSVincent Cheng break; 4133a6ba7dcSVincent Cheng case 2: 4143a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0; 4153a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1; 4163a6ba7dcSVincent Cheng break; 4173a6ba7dcSVincent Cheng case 3: 4183a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0; 4193a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1; 4203a6ba7dcSVincent Cheng break; 4213a6ba7dcSVincent Cheng case 4: 4223a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0; 4233a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1; 4243a6ba7dcSVincent Cheng break; 4253a6ba7dcSVincent Cheng case 5: 4263a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0; 4273a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1; 4283a6ba7dcSVincent Cheng break; 4293a6ba7dcSVincent Cheng case 6: 4303a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0; 4313a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1; 4323a6ba7dcSVincent Cheng break; 4333a6ba7dcSVincent Cheng case 7: 4343a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0; 4353a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1; 4363a6ba7dcSVincent Cheng break; 4373a6ba7dcSVincent Cheng default: 4383a6ba7dcSVincent Cheng return -EINVAL; 4393a6ba7dcSVincent Cheng } 4403a6ba7dcSVincent Cheng 4413a6ba7dcSVincent Cheng val = SYNCTRL1_MASTER_SYNC_RST; 4423a6ba7dcSVincent Cheng 4433a6ba7dcSVincent Cheng /* Place master sync in reset */ 4443a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 4453a6ba7dcSVincent Cheng if (err) 4463a6ba7dcSVincent Cheng return err; 4473a6ba7dcSVincent Cheng 4483a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src)); 4493a6ba7dcSVincent Cheng if (err) 4503a6ba7dcSVincent Cheng return err; 4513a6ba7dcSVincent Cheng 4523a6ba7dcSVincent Cheng /* Set sync trigger mask */ 4533a6ba7dcSVincent Cheng val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG; 4543a6ba7dcSVincent Cheng 4553a6ba7dcSVincent Cheng if (qn) 4563a6ba7dcSVincent Cheng val |= SYNCTRL1_Q0_DIV_SYNC_TRIG; 4573a6ba7dcSVincent Cheng 4583a6ba7dcSVincent Cheng if (qn_plus_1) 4593a6ba7dcSVincent Cheng val |= SYNCTRL1_Q1_DIV_SYNC_TRIG; 4603a6ba7dcSVincent Cheng 4613a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 4623a6ba7dcSVincent Cheng if (err) 4633a6ba7dcSVincent Cheng return err; 4643a6ba7dcSVincent Cheng 4657ea5fda2SMin Li /* PLL5 can have OUT8 as second additional output. */ 4667ea5fda2SMin Li if ((pll == 5) && (qn_plus_1 != 0)) { 4677ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 4687ea5fda2SMin Li &temp, sizeof(temp)); 4697ea5fda2SMin Li if (err) 4707ea5fda2SMin Li return err; 4717ea5fda2SMin Li 4727ea5fda2SMin Li temp &= ~(Q9_TO_Q8_SYNC_TRIG); 4737ea5fda2SMin Li 4747ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 4757ea5fda2SMin Li &temp, sizeof(temp)); 4767ea5fda2SMin Li if (err) 4777ea5fda2SMin Li return err; 4787ea5fda2SMin Li 4797ea5fda2SMin Li temp |= Q9_TO_Q8_SYNC_TRIG; 4807ea5fda2SMin Li 4817ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 4827ea5fda2SMin Li &temp, sizeof(temp)); 4837ea5fda2SMin Li if (err) 4847ea5fda2SMin Li return err; 4857ea5fda2SMin Li } 4867ea5fda2SMin Li 4877ea5fda2SMin Li /* PLL6 can have OUT11 as second additional output. */ 4887ea5fda2SMin Li if ((pll == 6) && (qn_plus_1 != 0)) { 4897ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 4907ea5fda2SMin Li &temp, sizeof(temp)); 4917ea5fda2SMin Li if (err) 4927ea5fda2SMin Li return err; 4937ea5fda2SMin Li 4947ea5fda2SMin Li temp &= ~(Q10_TO_Q11_SYNC_TRIG); 4957ea5fda2SMin Li 4967ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 4977ea5fda2SMin Li &temp, sizeof(temp)); 4987ea5fda2SMin Li if (err) 4997ea5fda2SMin Li return err; 5007ea5fda2SMin Li 5017ea5fda2SMin Li temp |= Q10_TO_Q11_SYNC_TRIG; 5027ea5fda2SMin Li 5037ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 5047ea5fda2SMin Li &temp, sizeof(temp)); 5057ea5fda2SMin Li if (err) 5067ea5fda2SMin Li return err; 5077ea5fda2SMin Li } 5087ea5fda2SMin Li 5093a6ba7dcSVincent Cheng /* Place master sync out of reset */ 5103a6ba7dcSVincent Cheng val &= ~(SYNCTRL1_MASTER_SYNC_RST); 5113a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 5123a6ba7dcSVincent Cheng 5133a6ba7dcSVincent Cheng return err; 5143a6ba7dcSVincent Cheng } 5153a6ba7dcSVincent Cheng 5167ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src) 5177ea5fda2SMin Li { 5187ea5fda2SMin Li int err = 0; 5197ea5fda2SMin Li 5207ea5fda2SMin Li switch (tod_addr) { 5217ea5fda2SMin Li case TOD_0: 5227ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; 5237ea5fda2SMin Li break; 5247ea5fda2SMin Li case TOD_1: 5257ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; 5267ea5fda2SMin Li break; 5277ea5fda2SMin Li case TOD_2: 5287ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; 5297ea5fda2SMin Li break; 5307ea5fda2SMin Li case TOD_3: 5317ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; 5327ea5fda2SMin Li break; 5337ea5fda2SMin Li default: 5347ea5fda2SMin Li err = -EINVAL; 5357ea5fda2SMin Li } 5367ea5fda2SMin Li 5377ea5fda2SMin Li return err; 5387ea5fda2SMin Li } 5397ea5fda2SMin Li 5403a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel) 5413a6ba7dcSVincent Cheng { 5423a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 5433a6ba7dcSVincent Cheng 5443a6ba7dcSVincent Cheng u8 pll; 5453a6ba7dcSVincent Cheng u8 sync_src; 5463a6ba7dcSVincent Cheng u8 qn; 5473a6ba7dcSVincent Cheng u8 qn_plus_1; 5483a6ba7dcSVincent Cheng int err = 0; 5497ea5fda2SMin Li u8 out8_mux = 0; 5507ea5fda2SMin Li u8 out11_mux = 0; 5517ea5fda2SMin Li u8 temp; 5523a6ba7dcSVincent Cheng 5533a6ba7dcSVincent Cheng u16 output_mask = channel->output_mask; 5543a6ba7dcSVincent Cheng 5557ea5fda2SMin Li err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src); 5567ea5fda2SMin Li if (err) 5577ea5fda2SMin Li return err; 5587ea5fda2SMin Li 5597ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 5607ea5fda2SMin Li &temp, sizeof(temp)); 5617ea5fda2SMin Li if (err) 5627ea5fda2SMin Li return err; 5637ea5fda2SMin Li 5647ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 5657ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 5667ea5fda2SMin Li out8_mux = 1; 5677ea5fda2SMin Li 5687ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 5697ea5fda2SMin Li &temp, sizeof(temp)); 5707ea5fda2SMin Li if (err) 5717ea5fda2SMin Li return err; 5727ea5fda2SMin Li 5737ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 5747ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 5757ea5fda2SMin Li out11_mux = 1; 5763a6ba7dcSVincent Cheng 5773a6ba7dcSVincent Cheng for (pll = 0; pll < 8; pll++) { 5787ea5fda2SMin Li qn = 0; 5797ea5fda2SMin Li qn_plus_1 = 0; 5803a6ba7dcSVincent Cheng 5813a6ba7dcSVincent Cheng if (pll < 4) { 5823a6ba7dcSVincent Cheng /* First 4 pll has 2 outputs */ 5837ea5fda2SMin Li qn = output_mask & 0x1; 5847ea5fda2SMin Li output_mask = output_mask >> 1; 5853a6ba7dcSVincent Cheng qn_plus_1 = output_mask & 0x1; 5863a6ba7dcSVincent Cheng output_mask = output_mask >> 1; 5877ea5fda2SMin Li } else if (pll == 4) { 5887ea5fda2SMin Li if (out8_mux == 0) { 5897ea5fda2SMin Li qn = output_mask & 0x1; 5907ea5fda2SMin Li output_mask = output_mask >> 1; 5917ea5fda2SMin Li } 5927ea5fda2SMin Li } else if (pll == 5) { 5937ea5fda2SMin Li if (out8_mux) { 5947ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 5957ea5fda2SMin Li output_mask = output_mask >> 1; 5967ea5fda2SMin Li } 5977ea5fda2SMin Li qn = output_mask & 0x1; 5987ea5fda2SMin Li output_mask = output_mask >> 1; 5997ea5fda2SMin Li } else if (pll == 6) { 6007ea5fda2SMin Li qn = output_mask & 0x1; 6017ea5fda2SMin Li output_mask = output_mask >> 1; 6027ea5fda2SMin Li if (out11_mux) { 6037ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 6047ea5fda2SMin Li output_mask = output_mask >> 1; 6057ea5fda2SMin Li } 6067ea5fda2SMin Li } else if (pll == 7) { 6077ea5fda2SMin Li if (out11_mux == 0) { 6087ea5fda2SMin Li qn = output_mask & 0x1; 6097ea5fda2SMin Li output_mask = output_mask >> 1; 6107ea5fda2SMin Li } 6113a6ba7dcSVincent Cheng } 6123a6ba7dcSVincent Cheng 6133a6ba7dcSVincent Cheng if ((qn != 0) || (qn_plus_1 != 0)) 6143a6ba7dcSVincent Cheng err = _sync_pll_output(idtcm, pll, sync_src, qn, 6153a6ba7dcSVincent Cheng qn_plus_1); 6163a6ba7dcSVincent Cheng 6173a6ba7dcSVincent Cheng if (err) 6183a6ba7dcSVincent Cheng return err; 6193a6ba7dcSVincent Cheng } 6203a6ba7dcSVincent Cheng 6213a6ba7dcSVincent Cheng return err; 6223a6ba7dcSVincent Cheng } 6233a6ba7dcSVincent Cheng 6247ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel, 6253a6ba7dcSVincent Cheng struct timespec64 const *ts, 6263a6ba7dcSVincent Cheng enum hw_tod_write_trig_sel wr_trig) 6273a6ba7dcSVincent Cheng { 6283a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 6293a6ba7dcSVincent Cheng 6303a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 6313a6ba7dcSVincent Cheng u8 cmd; 6323a6ba7dcSVincent Cheng int err; 6333a6ba7dcSVincent Cheng struct timespec64 local_ts = *ts; 6343a6ba7dcSVincent Cheng s64 total_overhead_ns; 6353a6ba7dcSVincent Cheng 6363a6ba7dcSVincent Cheng /* Configure HW TOD write trigger. */ 6373a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6383a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6393a6ba7dcSVincent Cheng 6403a6ba7dcSVincent Cheng if (err) 6413a6ba7dcSVincent Cheng return err; 6423a6ba7dcSVincent Cheng 6433a6ba7dcSVincent Cheng cmd &= ~(0x0f); 6443a6ba7dcSVincent Cheng cmd |= wr_trig | 0x08; 6453a6ba7dcSVincent Cheng 6463a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6473a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6483a6ba7dcSVincent Cheng 6493a6ba7dcSVincent Cheng if (err) 6503a6ba7dcSVincent Cheng return err; 6513a6ba7dcSVincent Cheng 6523a6ba7dcSVincent Cheng if (wr_trig != HW_TOD_WR_TRIG_SEL_MSB) { 6533a6ba7dcSVincent Cheng 6543a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 6553a6ba7dcSVincent Cheng 6563a6ba7dcSVincent Cheng if (err) 6573a6ba7dcSVincent Cheng return err; 6583a6ba7dcSVincent Cheng 6593a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 6603a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 6613a6ba7dcSVincent Cheng 6623a6ba7dcSVincent Cheng if (err) 6633a6ba7dcSVincent Cheng return err; 6643a6ba7dcSVincent Cheng } 6653a6ba7dcSVincent Cheng 6663a6ba7dcSVincent Cheng /* ARM HW TOD write trigger. */ 6673a6ba7dcSVincent Cheng cmd &= ~(0x08); 6683a6ba7dcSVincent Cheng 6693a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6703a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6713a6ba7dcSVincent Cheng 6723a6ba7dcSVincent Cheng if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) { 6733a6ba7dcSVincent Cheng 6743a6ba7dcSVincent Cheng if (idtcm->calculate_overhead_flag) { 6751ece2fbeSVincent Cheng /* Assumption: I2C @ 400KHz */ 6767260d1c8SMin Li ktime_t diff = ktime_sub(ktime_get_raw(), 6777260d1c8SMin Li idtcm->start_time); 6787260d1c8SMin Li total_overhead_ns = ktime_to_ns(diff) 6793a6ba7dcSVincent Cheng + idtcm->tod_write_overhead_ns 6803a6ba7dcSVincent Cheng + SETTIME_CORRECTION; 6813a6ba7dcSVincent Cheng 6823a6ba7dcSVincent Cheng timespec64_add_ns(&local_ts, total_overhead_ns); 6833a6ba7dcSVincent Cheng 6843a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 6853a6ba7dcSVincent Cheng } 6863a6ba7dcSVincent Cheng 6873a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 6883a6ba7dcSVincent Cheng 6893a6ba7dcSVincent Cheng if (err) 6903a6ba7dcSVincent Cheng return err; 6913a6ba7dcSVincent Cheng 6923a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 6933a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 6943a6ba7dcSVincent Cheng } 6953a6ba7dcSVincent Cheng 6963a6ba7dcSVincent Cheng return err; 6973a6ba7dcSVincent Cheng } 6983a6ba7dcSVincent Cheng 6997ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, 7007ea5fda2SMin Li struct timespec64 const *ts, 7017ea5fda2SMin Li enum scsr_tod_write_trig_sel wr_trig, 7027ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 7037ea5fda2SMin Li { 7047ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 7057ea5fda2SMin Li unsigned char buf[TOD_BYTE_COUNT], cmd; 7067ea5fda2SMin Li struct timespec64 local_ts = *ts; 7077ea5fda2SMin Li int err, count = 0; 7087ea5fda2SMin Li 7097ea5fda2SMin Li timespec64_add_ns(&local_ts, SETTIME_CORRECTION); 7107ea5fda2SMin Li 7117ea5fda2SMin Li err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 7127ea5fda2SMin Li 7137ea5fda2SMin Li if (err) 7147ea5fda2SMin Li return err; 7157ea5fda2SMin Li 7167ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE, 7177ea5fda2SMin Li buf, sizeof(buf)); 7187ea5fda2SMin Li if (err) 7197ea5fda2SMin Li return err; 7207ea5fda2SMin Li 7217ea5fda2SMin Li /* Trigger the write operation. */ 7227ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7237ea5fda2SMin Li &cmd, sizeof(cmd)); 7247ea5fda2SMin Li if (err) 7257ea5fda2SMin Li return err; 7267ea5fda2SMin Li 7277ea5fda2SMin Li cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT); 7287ea5fda2SMin Li cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT); 7297ea5fda2SMin Li cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT); 7307ea5fda2SMin Li cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT); 7317ea5fda2SMin Li 7327ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD, 7337ea5fda2SMin Li &cmd, sizeof(cmd)); 7347ea5fda2SMin Li if (err) 7357ea5fda2SMin Li return err; 7367ea5fda2SMin Li 7377ea5fda2SMin Li /* Wait for the operation to complete. */ 7387ea5fda2SMin Li while (1) { 7397ea5fda2SMin Li /* pps trigger takes up to 1 sec to complete */ 7407ea5fda2SMin Li if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS) 7417ea5fda2SMin Li msleep(50); 7427ea5fda2SMin Li 7437ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7447ea5fda2SMin Li &cmd, sizeof(cmd)); 7457ea5fda2SMin Li if (err) 7467ea5fda2SMin Li return err; 7477ea5fda2SMin Li 748251f4fe2SMin Li if ((cmd & TOD_WRITE_SELECTION_MASK) == 0) 7497ea5fda2SMin Li break; 7507ea5fda2SMin Li 7517ea5fda2SMin Li if (++count > 20) { 7527ea5fda2SMin Li dev_err(&idtcm->client->dev, 7537ea5fda2SMin Li "Timed out waiting for the write counter\n"); 7547ea5fda2SMin Li return -EIO; 7557ea5fda2SMin Li } 7567ea5fda2SMin Li } 7577ea5fda2SMin Li 7587ea5fda2SMin Li return 0; 7597ea5fda2SMin Li } 7607ea5fda2SMin Li 7617260d1c8SMin Li static int get_output_base_addr(u8 outn) 7627260d1c8SMin Li { 7637260d1c8SMin Li int base; 7647260d1c8SMin Li 7657260d1c8SMin Li switch (outn) { 7667260d1c8SMin Li case 0: 7677260d1c8SMin Li base = OUTPUT_0; 7687260d1c8SMin Li break; 7697260d1c8SMin Li case 1: 7707260d1c8SMin Li base = OUTPUT_1; 7717260d1c8SMin Li break; 7727260d1c8SMin Li case 2: 7737260d1c8SMin Li base = OUTPUT_2; 7747260d1c8SMin Li break; 7757260d1c8SMin Li case 3: 7767260d1c8SMin Li base = OUTPUT_3; 7777260d1c8SMin Li break; 7787260d1c8SMin Li case 4: 7797260d1c8SMin Li base = OUTPUT_4; 7807260d1c8SMin Li break; 7817260d1c8SMin Li case 5: 7827260d1c8SMin Li base = OUTPUT_5; 7837260d1c8SMin Li break; 7847260d1c8SMin Li case 6: 7857260d1c8SMin Li base = OUTPUT_6; 7867260d1c8SMin Li break; 7877260d1c8SMin Li case 7: 7887260d1c8SMin Li base = OUTPUT_7; 7897260d1c8SMin Li break; 7907260d1c8SMin Li case 8: 7917260d1c8SMin Li base = OUTPUT_8; 7927260d1c8SMin Li break; 7937260d1c8SMin Li case 9: 7947260d1c8SMin Li base = OUTPUT_9; 7957260d1c8SMin Li break; 7967260d1c8SMin Li case 10: 7977260d1c8SMin Li base = OUTPUT_10; 7987260d1c8SMin Li break; 7997260d1c8SMin Li case 11: 8007260d1c8SMin Li base = OUTPUT_11; 8017260d1c8SMin Li break; 8027260d1c8SMin Li default: 8037260d1c8SMin Li base = -EINVAL; 8047260d1c8SMin Li } 8057260d1c8SMin Li 8067260d1c8SMin Li return base; 8077260d1c8SMin Li } 8087260d1c8SMin Li 809*da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel, 810251f4fe2SMin Li struct timespec64 const *ts) 8113a6ba7dcSVincent Cheng { 8123a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8133a6ba7dcSVincent Cheng int err; 8143a6ba7dcSVincent Cheng 815251f4fe2SMin Li err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB); 8163a6ba7dcSVincent Cheng 8177ea5fda2SMin Li if (err) { 8187ea5fda2SMin Li dev_err(&idtcm->client->dev, 819251f4fe2SMin Li "%s: Set HW ToD failed\n", __func__); 8203a6ba7dcSVincent Cheng return err; 8217ea5fda2SMin Li } 8223a6ba7dcSVincent Cheng 8237ea5fda2SMin Li return idtcm_sync_pps_output(channel); 8247ea5fda2SMin Li } 8253a6ba7dcSVincent Cheng 826*da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel, 8277ea5fda2SMin Li struct timespec64 const *ts, 8287ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 8297ea5fda2SMin Li { 8307ea5fda2SMin Li return _idtcm_set_dpll_scsr_tod(channel, ts, 8317ea5fda2SMin Li SCSR_TOD_WR_TRIG_SEL_IMMEDIATE, 8327ea5fda2SMin Li wr_type); 8333a6ba7dcSVincent Cheng } 8343a6ba7dcSVincent Cheng 8353a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel, 8363a6ba7dcSVincent Cheng s32 offset_ns) 8373a6ba7dcSVincent Cheng { 8383a6ba7dcSVincent Cheng int err; 8393a6ba7dcSVincent Cheng int i; 8403a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8413a6ba7dcSVincent Cheng 8423a6ba7dcSVincent Cheng u8 buf[4]; 8433a6ba7dcSVincent Cheng 8443a6ba7dcSVincent Cheng for (i = 0; i < 4; i++) { 8453a6ba7dcSVincent Cheng buf[i] = 0xff & (offset_ns); 8463a6ba7dcSVincent Cheng offset_ns >>= 8; 8473a6ba7dcSVincent Cheng } 8483a6ba7dcSVincent Cheng 8493a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET, 8503a6ba7dcSVincent Cheng buf, sizeof(buf)); 8513a6ba7dcSVincent Cheng 8523a6ba7dcSVincent Cheng return err; 8533a6ba7dcSVincent Cheng } 8543a6ba7dcSVincent Cheng 8553a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel, 8563a6ba7dcSVincent Cheng u32 max_ffo_ppb) 8573a6ba7dcSVincent Cheng { 8583a6ba7dcSVincent Cheng int err; 8593a6ba7dcSVincent Cheng u8 i; 8603a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8613a6ba7dcSVincent Cheng 8623a6ba7dcSVincent Cheng u8 buf[3]; 8633a6ba7dcSVincent Cheng 8643a6ba7dcSVincent Cheng if (max_ffo_ppb & 0xff000000) 8653a6ba7dcSVincent Cheng max_ffo_ppb = 0; 8663a6ba7dcSVincent Cheng 8673a6ba7dcSVincent Cheng for (i = 0; i < 3; i++) { 8683a6ba7dcSVincent Cheng buf[i] = 0xff & (max_ffo_ppb); 8693a6ba7dcSVincent Cheng max_ffo_ppb >>= 8; 8703a6ba7dcSVincent Cheng } 8713a6ba7dcSVincent Cheng 8723a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 8733a6ba7dcSVincent Cheng PULL_IN_SLOPE_LIMIT, buf, sizeof(buf)); 8743a6ba7dcSVincent Cheng 8753a6ba7dcSVincent Cheng return err; 8763a6ba7dcSVincent Cheng } 8773a6ba7dcSVincent Cheng 8783a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel) 8793a6ba7dcSVincent Cheng { 8803a6ba7dcSVincent Cheng int err; 8813a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8823a6ba7dcSVincent Cheng 8833a6ba7dcSVincent Cheng u8 buf; 8843a6ba7dcSVincent Cheng 8853a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL, 8863a6ba7dcSVincent Cheng &buf, sizeof(buf)); 8873a6ba7dcSVincent Cheng 8883a6ba7dcSVincent Cheng if (err) 8893a6ba7dcSVincent Cheng return err; 8903a6ba7dcSVincent Cheng 8913a6ba7dcSVincent Cheng if (buf == 0) { 8923a6ba7dcSVincent Cheng buf = 0x01; 8933a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 8943a6ba7dcSVincent Cheng PULL_IN_CTRL, &buf, sizeof(buf)); 8953a6ba7dcSVincent Cheng } else { 8963a6ba7dcSVincent Cheng err = -EBUSY; 8973a6ba7dcSVincent Cheng } 8983a6ba7dcSVincent Cheng 8993a6ba7dcSVincent Cheng return err; 9003a6ba7dcSVincent Cheng } 9013a6ba7dcSVincent Cheng 9023a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel, 9033a6ba7dcSVincent Cheng s32 offset_ns, 9043a6ba7dcSVincent Cheng u32 max_ffo_ppb) 9053a6ba7dcSVincent Cheng { 9063a6ba7dcSVincent Cheng int err; 9073a6ba7dcSVincent Cheng 9083a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_offset(channel, -offset_ns); 9093a6ba7dcSVincent Cheng 9103a6ba7dcSVincent Cheng if (err) 9113a6ba7dcSVincent Cheng return err; 9123a6ba7dcSVincent Cheng 9133a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb); 9143a6ba7dcSVincent Cheng 9153a6ba7dcSVincent Cheng if (err) 9163a6ba7dcSVincent Cheng return err; 9173a6ba7dcSVincent Cheng 9183a6ba7dcSVincent Cheng err = idtcm_start_phase_pull_in(channel); 9193a6ba7dcSVincent Cheng 9203a6ba7dcSVincent Cheng return err; 9213a6ba7dcSVincent Cheng } 9223a6ba7dcSVincent Cheng 9237ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel) 9247ea5fda2SMin Li { 9257ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 9267ea5fda2SMin Li s64 current_ns = 0; 9277ea5fda2SMin Li s64 lowest_ns = 0; 9287ea5fda2SMin Li int err; 9297ea5fda2SMin Li u8 i; 9307ea5fda2SMin Li 9317ea5fda2SMin Li ktime_t start; 9327ea5fda2SMin Li ktime_t stop; 9337260d1c8SMin Li ktime_t diff; 9347ea5fda2SMin Li 9357ea5fda2SMin Li char buf[TOD_BYTE_COUNT] = {0}; 9367ea5fda2SMin Li 9377ea5fda2SMin Li /* Set page offset */ 9387ea5fda2SMin Li idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0, 9397ea5fda2SMin Li buf, sizeof(buf)); 9407ea5fda2SMin Li 9417ea5fda2SMin Li for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) { 9427ea5fda2SMin Li 9437ea5fda2SMin Li start = ktime_get_raw(); 9447ea5fda2SMin Li 9457ea5fda2SMin Li err = idtcm_write(idtcm, channel->hw_dpll_n, 9467ea5fda2SMin Li HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 9477ea5fda2SMin Li 9487ea5fda2SMin Li if (err) 9497ea5fda2SMin Li return err; 9507ea5fda2SMin Li 9517ea5fda2SMin Li stop = ktime_get_raw(); 9527ea5fda2SMin Li 9537260d1c8SMin Li diff = ktime_sub(stop, start); 9547260d1c8SMin Li 9557260d1c8SMin Li current_ns = ktime_to_ns(diff); 9567ea5fda2SMin Li 9577ea5fda2SMin Li if (i == 0) { 9587ea5fda2SMin Li lowest_ns = current_ns; 9597ea5fda2SMin Li } else { 9607ea5fda2SMin Li if (current_ns < lowest_ns) 9617ea5fda2SMin Li lowest_ns = current_ns; 9627ea5fda2SMin Li } 9637ea5fda2SMin Li } 9647ea5fda2SMin Li 9657ea5fda2SMin Li idtcm->tod_write_overhead_ns = lowest_ns; 9667ea5fda2SMin Li 9677ea5fda2SMin Li return err; 9687ea5fda2SMin Li } 9697ea5fda2SMin Li 970*da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) 9713a6ba7dcSVincent Cheng { 9723a6ba7dcSVincent Cheng int err; 9733a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 9743a6ba7dcSVincent Cheng struct timespec64 ts; 9753a6ba7dcSVincent Cheng s64 now; 9763a6ba7dcSVincent Cheng 977*da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) { 9783a6ba7dcSVincent Cheng err = idtcm_do_phase_pull_in(channel, delta, 0); 9793a6ba7dcSVincent Cheng } else { 9803a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 1; 9813a6ba7dcSVincent Cheng 9827ea5fda2SMin Li err = set_tod_write_overhead(channel); 9837ea5fda2SMin Li 9847ea5fda2SMin Li if (err) 9857ea5fda2SMin Li return err; 9867ea5fda2SMin Li 9873a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, &ts); 9883a6ba7dcSVincent Cheng 9893a6ba7dcSVincent Cheng if (err) 9903a6ba7dcSVincent Cheng return err; 9913a6ba7dcSVincent Cheng 9923a6ba7dcSVincent Cheng now = timespec64_to_ns(&ts); 9933a6ba7dcSVincent Cheng now += delta; 9943a6ba7dcSVincent Cheng 9953a6ba7dcSVincent Cheng ts = ns_to_timespec64(now); 9963a6ba7dcSVincent Cheng 997*da948233SMin Li err = _idtcm_settime_deprecated(channel, &ts); 9983a6ba7dcSVincent Cheng } 9993a6ba7dcSVincent Cheng 10003a6ba7dcSVincent Cheng return err; 10013a6ba7dcSVincent Cheng } 10023a6ba7dcSVincent Cheng 10033a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm) 10043a6ba7dcSVincent Cheng { 10053a6ba7dcSVincent Cheng u8 byte = SM_RESET_CMD; 1006251f4fe2SMin Li u32 status = 0; 1007251f4fe2SMin Li int err; 1008251f4fe2SMin Li u8 i; 1009251f4fe2SMin Li 1010251f4fe2SMin Li clear_boot_status(idtcm); 10113a6ba7dcSVincent Cheng 10123a6ba7dcSVincent Cheng err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte)); 10133a6ba7dcSVincent Cheng 1014251f4fe2SMin Li if (!err) { 1015251f4fe2SMin Li for (i = 0; i < 30; i++) { 1016251f4fe2SMin Li msleep_interruptible(100); 1017251f4fe2SMin Li read_boot_status(idtcm, &status); 1018251f4fe2SMin Li 1019251f4fe2SMin Li if (status == 0xA0) { 1020251f4fe2SMin Li dev_dbg(&idtcm->client->dev, 1021251f4fe2SMin Li "SM_RESET completed in %d ms\n", 1022251f4fe2SMin Li i * 100); 1023251f4fe2SMin Li break; 1024251f4fe2SMin Li } 1025251f4fe2SMin Li } 1026251f4fe2SMin Li 1027251f4fe2SMin Li if (!status) 1028251f4fe2SMin Li dev_err(&idtcm->client->dev, "Timed out waiting for CM_RESET to complete\n"); 1029251f4fe2SMin Li } 10303a6ba7dcSVincent Cheng 10313a6ba7dcSVincent Cheng return err; 10323a6ba7dcSVincent Cheng } 10333a6ba7dcSVincent Cheng 10343a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id) 10353a6ba7dcSVincent Cheng { 10361ece2fbeSVincent Cheng return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8)); 10373a6ba7dcSVincent Cheng } 10383a6ba7dcSVincent Cheng 10393a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id) 10403a6ba7dcSVincent Cheng { 10413a6ba7dcSVincent Cheng int err; 10423a6ba7dcSVincent Cheng u8 buf[2] = {0}; 10433a6ba7dcSVincent Cheng 10443a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf)); 10453a6ba7dcSVincent Cheng 10463a6ba7dcSVincent Cheng *product_id = (buf[1] << 8) | buf[0]; 10473a6ba7dcSVincent Cheng 10483a6ba7dcSVincent Cheng return err; 10493a6ba7dcSVincent Cheng } 10503a6ba7dcSVincent Cheng 10513a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major) 10523a6ba7dcSVincent Cheng { 10533a6ba7dcSVincent Cheng int err; 10543a6ba7dcSVincent Cheng u8 buf = 0; 10553a6ba7dcSVincent Cheng 10563a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf)); 10573a6ba7dcSVincent Cheng 10583a6ba7dcSVincent Cheng *major = buf >> 1; 10593a6ba7dcSVincent Cheng 10603a6ba7dcSVincent Cheng return err; 10613a6ba7dcSVincent Cheng } 10623a6ba7dcSVincent Cheng 10633a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor) 10643a6ba7dcSVincent Cheng { 10653a6ba7dcSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8)); 10663a6ba7dcSVincent Cheng } 10673a6ba7dcSVincent Cheng 10683a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix) 10693a6ba7dcSVincent Cheng { 10703a6ba7dcSVincent Cheng return idtcm_read(idtcm, 10713a6ba7dcSVincent Cheng GENERAL_STATUS, 10723a6ba7dcSVincent Cheng HOTFIX_REL, 10733a6ba7dcSVincent Cheng hotfix, 10743a6ba7dcSVincent Cheng sizeof(u8)); 10753a6ba7dcSVincent Cheng } 10763a6ba7dcSVincent Cheng 10771ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm, 10781ece2fbeSVincent Cheng u8 *config_select) 10793a6ba7dcSVincent Cheng { 10801ece2fbeSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT, 10811ece2fbeSVincent Cheng config_select, sizeof(u8)); 10823a6ba7dcSVincent Cheng } 10833a6ba7dcSVincent Cheng 10843a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val) 10853a6ba7dcSVincent Cheng { 10863a6ba7dcSVincent Cheng int err = 0; 10873a6ba7dcSVincent Cheng 10883a6ba7dcSVincent Cheng switch (addr) { 10897ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR: 10903a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[0].output_mask, val); 10913a6ba7dcSVincent Cheng break; 10927ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR + 1: 10933a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[0].output_mask, val); 10943a6ba7dcSVincent Cheng break; 10957ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR: 10963a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[1].output_mask, val); 10973a6ba7dcSVincent Cheng break; 10987ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR + 1: 10993a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[1].output_mask, val); 11003a6ba7dcSVincent Cheng break; 11017ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR: 11023a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[2].output_mask, val); 11033a6ba7dcSVincent Cheng break; 11047ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR + 1: 11053a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[2].output_mask, val); 11063a6ba7dcSVincent Cheng break; 11077ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR: 11083a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[3].output_mask, val); 11093a6ba7dcSVincent Cheng break; 11107ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR + 1: 11113a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[3].output_mask, val); 11123a6ba7dcSVincent Cheng break; 11133a6ba7dcSVincent Cheng default: 11147ea5fda2SMin Li err = -EFAULT; /* Bad address */; 11153a6ba7dcSVincent Cheng break; 11163a6ba7dcSVincent Cheng } 11173a6ba7dcSVincent Cheng 11183a6ba7dcSVincent Cheng return err; 11193a6ba7dcSVincent Cheng } 11203a6ba7dcSVincent Cheng 11217ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll) 11227ea5fda2SMin Li { 11237ea5fda2SMin Li if (index >= MAX_TOD) { 11247ea5fda2SMin Li dev_err(&idtcm->client->dev, "ToD%d not supported\n", index); 11257ea5fda2SMin Li return -EINVAL; 11267ea5fda2SMin Li } 11277ea5fda2SMin Li 11287ea5fda2SMin Li if (pll >= MAX_PLL) { 11297ea5fda2SMin Li dev_err(&idtcm->client->dev, "Pll%d not supported\n", pll); 11307ea5fda2SMin Li return -EINVAL; 11317ea5fda2SMin Li } 11327ea5fda2SMin Li 11337ea5fda2SMin Li idtcm->channel[index].pll = pll; 11347ea5fda2SMin Li 11357ea5fda2SMin Li return 0; 11367ea5fda2SMin Li } 11377ea5fda2SMin Li 11383a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm, 11393a6ba7dcSVincent Cheng u16 regaddr, 11403a6ba7dcSVincent Cheng u8 val) 11413a6ba7dcSVincent Cheng { 11423a6ba7dcSVincent Cheng int err = 0; 11433a6ba7dcSVincent Cheng 11447ea5fda2SMin Li switch (regaddr) { 11457ea5fda2SMin Li case TOD_MASK_ADDR: 11467ea5fda2SMin Li if ((val & 0xf0) || !(val & 0x0f)) { 11477ea5fda2SMin Li dev_err(&idtcm->client->dev, 11487ea5fda2SMin Li "Invalid TOD mask 0x%hhx\n", val); 11497ea5fda2SMin Li err = -EINVAL; 11507ea5fda2SMin Li } else { 11517ea5fda2SMin Li idtcm->tod_mask = val; 11527ea5fda2SMin Li } 11537ea5fda2SMin Li break; 11547ea5fda2SMin Li case TOD0_PTP_PLL_ADDR: 11557ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 0, val); 11567ea5fda2SMin Li break; 11577ea5fda2SMin Li case TOD1_PTP_PLL_ADDR: 11587ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 1, val); 11597ea5fda2SMin Li break; 11607ea5fda2SMin Li case TOD2_PTP_PLL_ADDR: 11617ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 2, val); 11627ea5fda2SMin Li break; 11637ea5fda2SMin Li case TOD3_PTP_PLL_ADDR: 11647ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 3, val); 11657ea5fda2SMin Li break; 11667ea5fda2SMin Li default: 11677ea5fda2SMin Li err = set_pll_output_mask(idtcm, regaddr, val); 11687ea5fda2SMin Li break; 11693a6ba7dcSVincent Cheng } 11703a6ba7dcSVincent Cheng 11713a6ba7dcSVincent Cheng return err; 11723a6ba7dcSVincent Cheng } 11733a6ba7dcSVincent Cheng 11747ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm) 11753a6ba7dcSVincent Cheng { 11763a6ba7dcSVincent Cheng u8 i; 11773a6ba7dcSVincent Cheng u8 mask; 11783a6ba7dcSVincent Cheng 11797ea5fda2SMin Li dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x\n", idtcm->tod_mask); 11803a6ba7dcSVincent Cheng 11817ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 11823a6ba7dcSVincent Cheng mask = 1 << i; 11833a6ba7dcSVincent Cheng 11847ea5fda2SMin Li if (mask & idtcm->tod_mask) 11853a6ba7dcSVincent Cheng dev_dbg(&idtcm->client->dev, 11867ea5fda2SMin Li "TOD%d pll = %d output_mask = 0x%04x\n", 11877ea5fda2SMin Li i, idtcm->channel[i].pll, 11887ea5fda2SMin Li idtcm->channel[i].output_mask); 11893a6ba7dcSVincent Cheng } 11903a6ba7dcSVincent Cheng } 11913a6ba7dcSVincent Cheng 11923a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm, 11933a6ba7dcSVincent Cheng struct device *dev) 11943a6ba7dcSVincent Cheng { 11957ea5fda2SMin Li char fname[128] = FW_FILENAME; 11963a6ba7dcSVincent Cheng const struct firmware *fw; 11973a6ba7dcSVincent Cheng struct idtcm_fwrc *rec; 11983a6ba7dcSVincent Cheng u32 regaddr; 11993a6ba7dcSVincent Cheng int err; 12003a6ba7dcSVincent Cheng s32 len; 12013a6ba7dcSVincent Cheng u8 val; 12023a6ba7dcSVincent Cheng u8 loaddr; 12033a6ba7dcSVincent Cheng 12047ea5fda2SMin Li if (firmware) /* module parameter */ 12057ea5fda2SMin Li snprintf(fname, sizeof(fname), "%s", firmware); 12063a6ba7dcSVincent Cheng 12077ea5fda2SMin Li dev_dbg(&idtcm->client->dev, "requesting firmware '%s'\n", fname); 12083a6ba7dcSVincent Cheng 12097ea5fda2SMin Li err = request_firmware(&fw, fname, dev); 12107ea5fda2SMin Li 12117ea5fda2SMin Li if (err) { 12127ea5fda2SMin Li dev_err(&idtcm->client->dev, 12137ea5fda2SMin Li "Failed at line %d in func %s!\n", 12147ea5fda2SMin Li __LINE__, 12157ea5fda2SMin Li __func__); 12163a6ba7dcSVincent Cheng return err; 12177ea5fda2SMin Li } 12183a6ba7dcSVincent Cheng 12193a6ba7dcSVincent Cheng dev_dbg(&idtcm->client->dev, "firmware size %zu bytes\n", fw->size); 12203a6ba7dcSVincent Cheng 12213a6ba7dcSVincent Cheng rec = (struct idtcm_fwrc *) fw->data; 12223a6ba7dcSVincent Cheng 1223251f4fe2SMin Li if (contains_full_configuration(fw)) 12243a6ba7dcSVincent Cheng idtcm_state_machine_reset(idtcm); 12253a6ba7dcSVincent Cheng 12263a6ba7dcSVincent Cheng for (len = fw->size; len > 0; len -= sizeof(*rec)) { 12273a6ba7dcSVincent Cheng 12283a6ba7dcSVincent Cheng if (rec->reserved) { 12293a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 12303a6ba7dcSVincent Cheng "bad firmware, reserved field non-zero\n"); 12313a6ba7dcSVincent Cheng err = -EINVAL; 12323a6ba7dcSVincent Cheng } else { 12333a6ba7dcSVincent Cheng regaddr = rec->hiaddr << 8; 12343a6ba7dcSVincent Cheng regaddr |= rec->loaddr; 12353a6ba7dcSVincent Cheng 12363a6ba7dcSVincent Cheng val = rec->value; 12373a6ba7dcSVincent Cheng loaddr = rec->loaddr; 12383a6ba7dcSVincent Cheng 12393a6ba7dcSVincent Cheng rec++; 12403a6ba7dcSVincent Cheng 12413a6ba7dcSVincent Cheng err = check_and_set_masks(idtcm, regaddr, val); 12423a6ba7dcSVincent Cheng } 12433a6ba7dcSVincent Cheng 12447ea5fda2SMin Li if (err != -EINVAL) { 12457ea5fda2SMin Li err = 0; 12467ea5fda2SMin Li 12473a6ba7dcSVincent Cheng /* Top (status registers) and bottom are read-only */ 12483a6ba7dcSVincent Cheng if ((regaddr < GPIO_USER_CONTROL) 12493a6ba7dcSVincent Cheng || (regaddr >= SCRATCH)) 12503a6ba7dcSVincent Cheng continue; 12513a6ba7dcSVincent Cheng 12523a6ba7dcSVincent Cheng /* Page size 128, last 4 bytes of page skipped */ 12533a6ba7dcSVincent Cheng if (((loaddr > 0x7b) && (loaddr <= 0x7f)) 12543e14462fSYang Yingliang || loaddr > 0xfb) 12553a6ba7dcSVincent Cheng continue; 12563a6ba7dcSVincent Cheng 12573a6ba7dcSVincent Cheng err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val)); 12583a6ba7dcSVincent Cheng } 12593a6ba7dcSVincent Cheng 12603a6ba7dcSVincent Cheng if (err) 12613a6ba7dcSVincent Cheng goto out; 12623a6ba7dcSVincent Cheng } 12633a6ba7dcSVincent Cheng 12647ea5fda2SMin Li display_pll_and_masks(idtcm); 12653a6ba7dcSVincent Cheng 12663a6ba7dcSVincent Cheng out: 12673a6ba7dcSVincent Cheng release_firmware(fw); 12683a6ba7dcSVincent Cheng return err; 12693a6ba7dcSVincent Cheng } 12703a6ba7dcSVincent Cheng 12717ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel, 12727ea5fda2SMin Li bool enable, unsigned int outn) 12733a6ba7dcSVincent Cheng { 12743a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 12757260d1c8SMin Li int base; 12763a6ba7dcSVincent Cheng int err; 12777ea5fda2SMin Li u8 val; 12783a6ba7dcSVincent Cheng 12797260d1c8SMin Li base = get_output_base_addr(outn); 12807260d1c8SMin Li 12817260d1c8SMin Li if (!(base > 0)) { 12827260d1c8SMin Li dev_err(&idtcm->client->dev, 12837260d1c8SMin Li "%s - Unsupported out%d", __func__, outn); 12847260d1c8SMin Li return base; 12857260d1c8SMin Li } 12867260d1c8SMin Li 12877260d1c8SMin Li err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 12883a6ba7dcSVincent Cheng 12893a6ba7dcSVincent Cheng if (err) 12903a6ba7dcSVincent Cheng return err; 12913a6ba7dcSVincent Cheng 12923a6ba7dcSVincent Cheng if (enable) 12933a6ba7dcSVincent Cheng val |= SQUELCH_DISABLE; 12943a6ba7dcSVincent Cheng else 12953a6ba7dcSVincent Cheng val &= ~SQUELCH_DISABLE; 12963a6ba7dcSVincent Cheng 12977260d1c8SMin Li return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 12987ea5fda2SMin Li } 12997ea5fda2SMin Li 13007ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel, 13017ea5fda2SMin Li bool enable) 13027ea5fda2SMin Li { 13037ea5fda2SMin Li u16 mask; 13047ea5fda2SMin Li int err; 13057ea5fda2SMin Li u8 outn; 13067ea5fda2SMin Li 13077ea5fda2SMin Li mask = channel->output_mask; 13087ea5fda2SMin Li outn = 0; 13097ea5fda2SMin Li 13107ea5fda2SMin Li while (mask) { 13117ea5fda2SMin Li 13127ea5fda2SMin Li if (mask & 0x1) { 13137ea5fda2SMin Li 13147ea5fda2SMin Li err = idtcm_output_enable(channel, enable, outn); 13153a6ba7dcSVincent Cheng 13163a6ba7dcSVincent Cheng if (err) 13173a6ba7dcSVincent Cheng return err; 13187ea5fda2SMin Li } 13197ea5fda2SMin Li 13207ea5fda2SMin Li mask >>= 0x1; 13217ea5fda2SMin Li outn++; 13227ea5fda2SMin Li } 13233a6ba7dcSVincent Cheng 13243a6ba7dcSVincent Cheng return 0; 13253a6ba7dcSVincent Cheng } 13263a6ba7dcSVincent Cheng 13277ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel, 13287ea5fda2SMin Li bool enable, 13297ea5fda2SMin Li struct ptp_perout_request *perout) 13307ea5fda2SMin Li { 13317ea5fda2SMin Li unsigned int flags = perout->flags; 13327ea5fda2SMin Li 13337ea5fda2SMin Li if (flags == PEROUT_ENABLE_OUTPUT_MASK) 13347ea5fda2SMin Li return idtcm_output_mask_enable(channel, enable); 13357ea5fda2SMin Li 13367ea5fda2SMin Li /* Enable/disable individual output instead */ 13377ea5fda2SMin Li return idtcm_output_enable(channel, enable, perout->index); 13387ea5fda2SMin Li } 13397ea5fda2SMin Li 13407260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel, 13417260d1c8SMin Li enum pll_mode *pll_mode) 13427260d1c8SMin Li { 13437260d1c8SMin Li struct idtcm *idtcm = channel->idtcm; 13447260d1c8SMin Li int err; 13457260d1c8SMin Li u8 dpll_mode; 13467260d1c8SMin Li 13477260d1c8SMin Li err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13487260d1c8SMin Li &dpll_mode, sizeof(dpll_mode)); 13497260d1c8SMin Li if (err) 13507260d1c8SMin Li return err; 13517260d1c8SMin Li 13527260d1c8SMin Li *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; 13537260d1c8SMin Li 13547260d1c8SMin Li return 0; 13557260d1c8SMin Li } 13567260d1c8SMin Li 13573a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel, 13583a6ba7dcSVincent Cheng enum pll_mode pll_mode) 13593a6ba7dcSVincent Cheng { 13603a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 13613a6ba7dcSVincent Cheng int err; 13623a6ba7dcSVincent Cheng u8 dpll_mode; 13633a6ba7dcSVincent Cheng 13643a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13653a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 13663a6ba7dcSVincent Cheng if (err) 13673a6ba7dcSVincent Cheng return err; 13683a6ba7dcSVincent Cheng 13693a6ba7dcSVincent Cheng dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); 13703a6ba7dcSVincent Cheng 13713a6ba7dcSVincent Cheng dpll_mode |= (pll_mode << PLL_MODE_SHIFT); 13723a6ba7dcSVincent Cheng 13733a6ba7dcSVincent Cheng channel->pll_mode = pll_mode; 13743a6ba7dcSVincent Cheng 13753a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE, 13763a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 13773a6ba7dcSVincent Cheng if (err) 13783a6ba7dcSVincent Cheng return err; 13793a6ba7dcSVincent Cheng 13803a6ba7dcSVincent Cheng return 0; 13813a6ba7dcSVincent Cheng } 13823a6ba7dcSVincent Cheng 13833a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */ 13843a6ba7dcSVincent Cheng 1385425d2b1cSVincent Cheng /** 1386425d2b1cSVincent Cheng * @brief Maximum absolute value for write phase offset in picoseconds 1387425d2b1cSVincent Cheng * 1388425d2b1cSVincent Cheng * Destination signed register is 32-bit register in resolution of 50ps 1389425d2b1cSVincent Cheng * 1390425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1391425d2b1cSVincent Cheng */ 1392425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) 1393425d2b1cSVincent Cheng { 1394425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1395425d2b1cSVincent Cheng 1396425d2b1cSVincent Cheng int err; 1397425d2b1cSVincent Cheng u8 i; 1398425d2b1cSVincent Cheng u8 buf[4] = {0}; 1399425d2b1cSVincent Cheng s32 phase_50ps; 1400425d2b1cSVincent Cheng s64 offset_ps; 1401425d2b1cSVincent Cheng 1402425d2b1cSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_PHASE) { 1403425d2b1cSVincent Cheng 1404425d2b1cSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); 1405425d2b1cSVincent Cheng 1406425d2b1cSVincent Cheng if (err) 1407425d2b1cSVincent Cheng return err; 1408425d2b1cSVincent Cheng } 1409425d2b1cSVincent Cheng 1410425d2b1cSVincent Cheng offset_ps = (s64)delta_ns * 1000; 1411425d2b1cSVincent Cheng 1412425d2b1cSVincent Cheng /* 1413425d2b1cSVincent Cheng * Check for 32-bit signed max * 50: 1414425d2b1cSVincent Cheng * 1415425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1416425d2b1cSVincent Cheng */ 1417425d2b1cSVincent Cheng if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS) 1418425d2b1cSVincent Cheng offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS; 1419425d2b1cSVincent Cheng else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS) 1420425d2b1cSVincent Cheng offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS; 1421425d2b1cSVincent Cheng 14227260d1c8SMin Li phase_50ps = div_s64(offset_ps, 50); 1423425d2b1cSVincent Cheng 1424425d2b1cSVincent Cheng for (i = 0; i < 4; i++) { 1425425d2b1cSVincent Cheng buf[i] = phase_50ps & 0xff; 1426425d2b1cSVincent Cheng phase_50ps >>= 8; 1427425d2b1cSVincent Cheng } 1428425d2b1cSVincent Cheng 1429425d2b1cSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE, 1430425d2b1cSVincent Cheng buf, sizeof(buf)); 1431425d2b1cSVincent Cheng 1432425d2b1cSVincent Cheng return err; 1433425d2b1cSVincent Cheng } 1434425d2b1cSVincent Cheng 14357ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm) 14363a6ba7dcSVincent Cheng { 14373a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 14383a6ba7dcSVincent Cheng u8 i; 14393a6ba7dcSVincent Cheng int err; 14403a6ba7dcSVincent Cheng u8 buf[6] = {0}; 14413a6ba7dcSVincent Cheng s64 fcw; 14423a6ba7dcSVincent Cheng 14433a6ba7dcSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) { 14443a6ba7dcSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); 14453a6ba7dcSVincent Cheng if (err) 14463a6ba7dcSVincent Cheng return err; 14473a6ba7dcSVincent Cheng } 14483a6ba7dcSVincent Cheng 14493a6ba7dcSVincent Cheng /* 14503a6ba7dcSVincent Cheng * Frequency Control Word unit is: 1.11 * 10^-10 ppm 14513a6ba7dcSVincent Cheng * 14523a6ba7dcSVincent Cheng * adjfreq: 14533a6ba7dcSVincent Cheng * ppb * 10^9 14543a6ba7dcSVincent Cheng * FCW = ---------- 14553a6ba7dcSVincent Cheng * 111 14563a6ba7dcSVincent Cheng * 14573a6ba7dcSVincent Cheng * adjfine: 14583a6ba7dcSVincent Cheng * ppm_16 * 5^12 14593a6ba7dcSVincent Cheng * FCW = ------------- 14603a6ba7dcSVincent Cheng * 111 * 2^4 14613a6ba7dcSVincent Cheng */ 14623a6ba7dcSVincent Cheng 14633a6ba7dcSVincent Cheng /* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */ 14647ea5fda2SMin Li fcw = scaled_ppm * 244140625ULL; 14653a6ba7dcSVincent Cheng 14667260d1c8SMin Li fcw = div_s64(fcw, 1776); 14673a6ba7dcSVincent Cheng 14683a6ba7dcSVincent Cheng for (i = 0; i < 6; i++) { 14693a6ba7dcSVincent Cheng buf[i] = fcw & 0xff; 14703a6ba7dcSVincent Cheng fcw >>= 8; 14713a6ba7dcSVincent Cheng } 14723a6ba7dcSVincent Cheng 14733a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ, 14743a6ba7dcSVincent Cheng buf, sizeof(buf)); 14753a6ba7dcSVincent Cheng 14763a6ba7dcSVincent Cheng return err; 14773a6ba7dcSVincent Cheng } 14783a6ba7dcSVincent Cheng 14793a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 14803a6ba7dcSVincent Cheng { 14813a6ba7dcSVincent Cheng struct idtcm_channel *channel = 14823a6ba7dcSVincent Cheng container_of(ptp, struct idtcm_channel, caps); 14833a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 14843a6ba7dcSVincent Cheng int err; 14853a6ba7dcSVincent Cheng 14863a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 14873a6ba7dcSVincent Cheng 14883a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, ts); 14893a6ba7dcSVincent Cheng 14907ea5fda2SMin Li if (err) 14917ea5fda2SMin Li dev_err(&idtcm->client->dev, 14927ea5fda2SMin Li "Failed at line %d in func %s!\n", 14937ea5fda2SMin Li __LINE__, 14947ea5fda2SMin Li __func__); 14957ea5fda2SMin Li 14963a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 14973a6ba7dcSVincent Cheng 14983a6ba7dcSVincent Cheng return err; 14993a6ba7dcSVincent Cheng } 15003a6ba7dcSVincent Cheng 1501*da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp, 15023a6ba7dcSVincent Cheng const struct timespec64 *ts) 15033a6ba7dcSVincent Cheng { 15043a6ba7dcSVincent Cheng struct idtcm_channel *channel = 15053a6ba7dcSVincent Cheng container_of(ptp, struct idtcm_channel, caps); 15063a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15073a6ba7dcSVincent Cheng int err; 15083a6ba7dcSVincent Cheng 15093a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 15103a6ba7dcSVincent Cheng 1511*da948233SMin Li err = _idtcm_settime_deprecated(channel, ts); 15123a6ba7dcSVincent Cheng 15137ea5fda2SMin Li if (err) 15147ea5fda2SMin Li dev_err(&idtcm->client->dev, 15157ea5fda2SMin Li "Failed at line %d in func %s!\n", 15167ea5fda2SMin Li __LINE__, 15177ea5fda2SMin Li __func__); 15187ea5fda2SMin Li 15197ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 15207ea5fda2SMin Li 15217ea5fda2SMin Li return err; 15227ea5fda2SMin Li } 15237ea5fda2SMin Li 1524*da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp, 15257ea5fda2SMin Li const struct timespec64 *ts) 15267ea5fda2SMin Li { 15277ea5fda2SMin Li struct idtcm_channel *channel = 15287ea5fda2SMin Li container_of(ptp, struct idtcm_channel, caps); 15297ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 15307ea5fda2SMin Li int err; 15317ea5fda2SMin Li 15327ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 15337ea5fda2SMin Li 1534*da948233SMin Li err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 1535*da948233SMin Li 1536*da948233SMin Li if (err) 1537*da948233SMin Li dev_err(&idtcm->client->dev, 1538*da948233SMin Li "Failed at line %d in func %s!\n", 1539*da948233SMin Li __LINE__, 1540*da948233SMin Li __func__); 1541*da948233SMin Li 1542*da948233SMin Li mutex_unlock(&idtcm->reg_lock); 1543*da948233SMin Li 1544*da948233SMin Li return err; 1545*da948233SMin Li } 1546*da948233SMin Li 1547*da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta) 1548*da948233SMin Li { 1549*da948233SMin Li struct idtcm_channel *channel = 1550*da948233SMin Li container_of(ptp, struct idtcm_channel, caps); 1551*da948233SMin Li struct idtcm *idtcm = channel->idtcm; 1552*da948233SMin Li int err; 1553*da948233SMin Li 1554*da948233SMin Li mutex_lock(&idtcm->reg_lock); 1555*da948233SMin Li 1556*da948233SMin Li err = _idtcm_adjtime_deprecated(channel, delta); 15577ea5fda2SMin Li 15587ea5fda2SMin Li if (err) 15597ea5fda2SMin Li dev_err(&idtcm->client->dev, 15607ea5fda2SMin Li "Failed at line %d in func %s!\n", 15617ea5fda2SMin Li __LINE__, 15627ea5fda2SMin Li __func__); 15637ea5fda2SMin Li 15643a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 15653a6ba7dcSVincent Cheng 15663a6ba7dcSVincent Cheng return err; 15673a6ba7dcSVincent Cheng } 15683a6ba7dcSVincent Cheng 15693a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta) 15703a6ba7dcSVincent Cheng { 15713a6ba7dcSVincent Cheng struct idtcm_channel *channel = 15723a6ba7dcSVincent Cheng container_of(ptp, struct idtcm_channel, caps); 15733a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15747ea5fda2SMin Li struct timespec64 ts; 15757ea5fda2SMin Li enum scsr_tod_write_type_sel type; 15767ea5fda2SMin Li int err; 15777ea5fda2SMin Li 1578*da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { 15797ea5fda2SMin Li err = idtcm_do_phase_pull_in(channel, delta, 0); 15807ea5fda2SMin Li if (err) 15817ea5fda2SMin Li dev_err(&idtcm->client->dev, 15827ea5fda2SMin Li "Failed at line %d in func %s!\n", 15837ea5fda2SMin Li __LINE__, 15847ea5fda2SMin Li __func__); 15857ea5fda2SMin Li return err; 15867ea5fda2SMin Li } 15877ea5fda2SMin Li 15887ea5fda2SMin Li if (delta >= 0) { 15897ea5fda2SMin Li ts = ns_to_timespec64(delta); 15907ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; 15917ea5fda2SMin Li } else { 15927ea5fda2SMin Li ts = ns_to_timespec64(-delta); 15937ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; 15947ea5fda2SMin Li } 15957ea5fda2SMin Li 15967ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 15977ea5fda2SMin Li 1598*da948233SMin Li err = _idtcm_settime(channel, &ts, type); 15997ea5fda2SMin Li 16007ea5fda2SMin Li if (err) 16017ea5fda2SMin Li dev_err(&idtcm->client->dev, 16027ea5fda2SMin Li "Failed at line %d in func %s!\n", 16037ea5fda2SMin Li __LINE__, 16047ea5fda2SMin Li __func__); 16057ea5fda2SMin Li 16063a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 16073a6ba7dcSVincent Cheng 16083a6ba7dcSVincent Cheng return err; 16093a6ba7dcSVincent Cheng } 16103a6ba7dcSVincent Cheng 1611425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta) 1612425d2b1cSVincent Cheng { 1613425d2b1cSVincent Cheng struct idtcm_channel *channel = 1614425d2b1cSVincent Cheng container_of(ptp, struct idtcm_channel, caps); 1615425d2b1cSVincent Cheng 1616425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1617425d2b1cSVincent Cheng 1618425d2b1cSVincent Cheng int err; 1619425d2b1cSVincent Cheng 1620425d2b1cSVincent Cheng mutex_lock(&idtcm->reg_lock); 1621425d2b1cSVincent Cheng 1622425d2b1cSVincent Cheng err = _idtcm_adjphase(channel, delta); 1623425d2b1cSVincent Cheng 16247ea5fda2SMin Li if (err) 16257ea5fda2SMin Li dev_err(&idtcm->client->dev, 16267ea5fda2SMin Li "Failed at line %d in func %s!\n", 16277ea5fda2SMin Li __LINE__, 16287ea5fda2SMin Li __func__); 16297ea5fda2SMin Li 16307ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 16317ea5fda2SMin Li 16327ea5fda2SMin Li return err; 16337ea5fda2SMin Li } 16347ea5fda2SMin Li 16357ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 16367ea5fda2SMin Li { 16377ea5fda2SMin Li struct idtcm_channel *channel = 16387ea5fda2SMin Li container_of(ptp, struct idtcm_channel, caps); 16397ea5fda2SMin Li 16407ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 16417ea5fda2SMin Li 16427ea5fda2SMin Li int err; 16437ea5fda2SMin Li 16447ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 16457ea5fda2SMin Li 16467ea5fda2SMin Li err = _idtcm_adjfine(channel, scaled_ppm); 16477ea5fda2SMin Li 16487ea5fda2SMin Li if (err) 16497ea5fda2SMin Li dev_err(&idtcm->client->dev, 16507ea5fda2SMin Li "Failed at line %d in func %s!\n", 16517ea5fda2SMin Li __LINE__, 16527ea5fda2SMin Li __func__); 16537ea5fda2SMin Li 1654425d2b1cSVincent Cheng mutex_unlock(&idtcm->reg_lock); 1655425d2b1cSVincent Cheng 1656425d2b1cSVincent Cheng return err; 1657425d2b1cSVincent Cheng } 1658425d2b1cSVincent Cheng 16593a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp, 16603a6ba7dcSVincent Cheng struct ptp_clock_request *rq, int on) 16613a6ba7dcSVincent Cheng { 16627ea5fda2SMin Li int err; 16637ea5fda2SMin Li 16643a6ba7dcSVincent Cheng struct idtcm_channel *channel = 16653a6ba7dcSVincent Cheng container_of(ptp, struct idtcm_channel, caps); 16663a6ba7dcSVincent Cheng 16673a6ba7dcSVincent Cheng switch (rq->type) { 16683a6ba7dcSVincent Cheng case PTP_CLK_REQ_PEROUT: 16697ea5fda2SMin Li if (!on) { 16707ea5fda2SMin Li err = idtcm_perout_enable(channel, false, &rq->perout); 16717ea5fda2SMin Li if (err) 16727ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16737ea5fda2SMin Li "Failed at line %d in func %s!\n", 16747ea5fda2SMin Li __LINE__, 16757ea5fda2SMin Li __func__); 16767ea5fda2SMin Li return err; 16777ea5fda2SMin Li } 16783a6ba7dcSVincent Cheng 16793a6ba7dcSVincent Cheng /* Only accept a 1-PPS aligned to the second. */ 16803a6ba7dcSVincent Cheng if (rq->perout.start.nsec || rq->perout.period.sec != 1 || 16813a6ba7dcSVincent Cheng rq->perout.period.nsec) 16823a6ba7dcSVincent Cheng return -ERANGE; 16833a6ba7dcSVincent Cheng 16847ea5fda2SMin Li err = idtcm_perout_enable(channel, true, &rq->perout); 16857ea5fda2SMin Li if (err) 16867ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16877ea5fda2SMin Li "Failed at line %d in func %s!\n", 16887ea5fda2SMin Li __LINE__, 16897ea5fda2SMin Li __func__); 16907ea5fda2SMin Li return err; 16913a6ba7dcSVincent Cheng default: 16923a6ba7dcSVincent Cheng break; 16933a6ba7dcSVincent Cheng } 16943a6ba7dcSVincent Cheng 16953a6ba7dcSVincent Cheng return -EOPNOTSUPP; 16963a6ba7dcSVincent Cheng } 16973a6ba7dcSVincent Cheng 16987ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm, 16997ea5fda2SMin Li u8 pll, 17007ea5fda2SMin Li u8 sync_src, 17017ea5fda2SMin Li u8 qn, 17027ea5fda2SMin Li u8 qn_plus_1) 17037ea5fda2SMin Li { 17047ea5fda2SMin Li int err; 17057ea5fda2SMin Li u8 val; 17067ea5fda2SMin Li u16 dpll; 17077ea5fda2SMin Li u16 out0 = 0, out1 = 0; 17087ea5fda2SMin Li 17097ea5fda2SMin Li if ((qn == 0) && (qn_plus_1 == 0)) 17107ea5fda2SMin Li return 0; 17117ea5fda2SMin Li 17127ea5fda2SMin Li switch (pll) { 17137ea5fda2SMin Li case 0: 17147ea5fda2SMin Li dpll = DPLL_0; 17157ea5fda2SMin Li if (qn) 17167ea5fda2SMin Li out0 = OUTPUT_0; 17177ea5fda2SMin Li if (qn_plus_1) 17187ea5fda2SMin Li out1 = OUTPUT_1; 17197ea5fda2SMin Li break; 17207ea5fda2SMin Li case 1: 17217ea5fda2SMin Li dpll = DPLL_1; 17227ea5fda2SMin Li if (qn) 17237ea5fda2SMin Li out0 = OUTPUT_2; 17247ea5fda2SMin Li if (qn_plus_1) 17257ea5fda2SMin Li out1 = OUTPUT_3; 17267ea5fda2SMin Li break; 17277ea5fda2SMin Li case 2: 17287ea5fda2SMin Li dpll = DPLL_2; 17297ea5fda2SMin Li if (qn) 17307ea5fda2SMin Li out0 = OUTPUT_4; 17317ea5fda2SMin Li if (qn_plus_1) 17327ea5fda2SMin Li out1 = OUTPUT_5; 17337ea5fda2SMin Li break; 17347ea5fda2SMin Li case 3: 17357ea5fda2SMin Li dpll = DPLL_3; 17367ea5fda2SMin Li if (qn) 17377ea5fda2SMin Li out0 = OUTPUT_6; 17387ea5fda2SMin Li if (qn_plus_1) 17397ea5fda2SMin Li out1 = OUTPUT_7; 17407ea5fda2SMin Li break; 17417ea5fda2SMin Li case 4: 17427ea5fda2SMin Li dpll = DPLL_4; 17437ea5fda2SMin Li if (qn) 17447ea5fda2SMin Li out0 = OUTPUT_8; 17457ea5fda2SMin Li break; 17467ea5fda2SMin Li case 5: 17477ea5fda2SMin Li dpll = DPLL_5; 17487ea5fda2SMin Li if (qn) 17497ea5fda2SMin Li out0 = OUTPUT_9; 17507ea5fda2SMin Li if (qn_plus_1) 17517ea5fda2SMin Li out1 = OUTPUT_8; 17527ea5fda2SMin Li break; 17537ea5fda2SMin Li case 6: 17547ea5fda2SMin Li dpll = DPLL_6; 17557ea5fda2SMin Li if (qn) 17567ea5fda2SMin Li out0 = OUTPUT_10; 17577ea5fda2SMin Li if (qn_plus_1) 17587ea5fda2SMin Li out1 = OUTPUT_11; 17597ea5fda2SMin Li break; 17607ea5fda2SMin Li case 7: 17617ea5fda2SMin Li dpll = DPLL_7; 17627ea5fda2SMin Li if (qn) 17637ea5fda2SMin Li out0 = OUTPUT_11; 17647ea5fda2SMin Li break; 17657ea5fda2SMin Li default: 17667ea5fda2SMin Li return -EINVAL; 17677ea5fda2SMin Li } 17687ea5fda2SMin Li 17697ea5fda2SMin Li /* 17707ea5fda2SMin Li * Enable OUTPUT OUT_SYNC. 17717ea5fda2SMin Li */ 17727ea5fda2SMin Li if (out0) { 17737ea5fda2SMin Li err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17747ea5fda2SMin Li 17757ea5fda2SMin Li if (err) 17767ea5fda2SMin Li return err; 17777ea5fda2SMin Li 17787ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17797ea5fda2SMin Li 17807ea5fda2SMin Li err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17817ea5fda2SMin Li 17827ea5fda2SMin Li if (err) 17837ea5fda2SMin Li return err; 17847ea5fda2SMin Li } 17857ea5fda2SMin Li 17867ea5fda2SMin Li if (out1) { 17877ea5fda2SMin Li err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17887ea5fda2SMin Li 17897ea5fda2SMin Li if (err) 17907ea5fda2SMin Li return err; 17917ea5fda2SMin Li 17927ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17937ea5fda2SMin Li 17947ea5fda2SMin Li err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17957ea5fda2SMin Li 17967ea5fda2SMin Li if (err) 17977ea5fda2SMin Li return err; 17987ea5fda2SMin Li } 17997ea5fda2SMin Li 18007ea5fda2SMin Li /* enable dpll sync tod pps, must be set before dpll_mode */ 18017ea5fda2SMin Li err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 18027ea5fda2SMin Li if (err) 18037ea5fda2SMin Li return err; 18047ea5fda2SMin Li 18057ea5fda2SMin Li val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT); 18067ea5fda2SMin Li val |= (sync_src << TOD_SYNC_SOURCE_SHIFT); 18077ea5fda2SMin Li val |= TOD_SYNC_EN; 18087ea5fda2SMin Li 18097ea5fda2SMin Li return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 18107ea5fda2SMin Li } 18117ea5fda2SMin Li 18127ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel) 18137ea5fda2SMin Li { 18147ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 18157ea5fda2SMin Li 18167ea5fda2SMin Li u8 pll; 18177ea5fda2SMin Li u8 sync_src; 18187ea5fda2SMin Li u8 qn; 18197ea5fda2SMin Li u8 qn_plus_1; 18207ea5fda2SMin Li u8 cfg; 18217ea5fda2SMin Li int err = 0; 18227ea5fda2SMin Li u16 output_mask = channel->output_mask; 18237ea5fda2SMin Li u8 out8_mux = 0; 18247ea5fda2SMin Li u8 out11_mux = 0; 18257ea5fda2SMin Li u8 temp; 18267ea5fda2SMin Li 18277ea5fda2SMin Li /* 18287ea5fda2SMin Li * set tod_out_sync_enable to 0. 18297ea5fda2SMin Li */ 18307ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18317ea5fda2SMin Li if (err) 18327ea5fda2SMin Li return err; 18337ea5fda2SMin Li 18347ea5fda2SMin Li cfg &= ~TOD_OUT_SYNC_ENABLE; 18357ea5fda2SMin Li 18367ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18377ea5fda2SMin Li if (err) 18387ea5fda2SMin Li return err; 18397ea5fda2SMin Li 18407ea5fda2SMin Li switch (channel->tod_n) { 18417ea5fda2SMin Li case TOD_0: 18427ea5fda2SMin Li sync_src = 0; 18437ea5fda2SMin Li break; 18447ea5fda2SMin Li case TOD_1: 18457ea5fda2SMin Li sync_src = 1; 18467ea5fda2SMin Li break; 18477ea5fda2SMin Li case TOD_2: 18487ea5fda2SMin Li sync_src = 2; 18497ea5fda2SMin Li break; 18507ea5fda2SMin Li case TOD_3: 18517ea5fda2SMin Li sync_src = 3; 18527ea5fda2SMin Li break; 18537ea5fda2SMin Li default: 18547ea5fda2SMin Li return -EINVAL; 18557ea5fda2SMin Li } 18567ea5fda2SMin Li 18577ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 18587ea5fda2SMin Li &temp, sizeof(temp)); 18597ea5fda2SMin Li if (err) 18607ea5fda2SMin Li return err; 18617ea5fda2SMin Li 18627ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18637ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18647ea5fda2SMin Li out8_mux = 1; 18657ea5fda2SMin Li 18667ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 18677ea5fda2SMin Li &temp, sizeof(temp)); 18687ea5fda2SMin Li if (err) 18697ea5fda2SMin Li return err; 18707ea5fda2SMin Li 18717ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18727ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18737ea5fda2SMin Li out11_mux = 1; 18747ea5fda2SMin Li 18757ea5fda2SMin Li for (pll = 0; pll < 8; pll++) { 18767ea5fda2SMin Li qn = 0; 18777ea5fda2SMin Li qn_plus_1 = 0; 18787ea5fda2SMin Li 18797ea5fda2SMin Li if (pll < 4) { 18807ea5fda2SMin Li /* First 4 pll has 2 outputs */ 18817ea5fda2SMin Li qn = output_mask & 0x1; 18827ea5fda2SMin Li output_mask = output_mask >> 1; 18837ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18847ea5fda2SMin Li output_mask = output_mask >> 1; 18857ea5fda2SMin Li } else if (pll == 4) { 18867ea5fda2SMin Li if (out8_mux == 0) { 18877ea5fda2SMin Li qn = output_mask & 0x1; 18887ea5fda2SMin Li output_mask = output_mask >> 1; 18897ea5fda2SMin Li } 18907ea5fda2SMin Li } else if (pll == 5) { 18917ea5fda2SMin Li if (out8_mux) { 18927ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18937ea5fda2SMin Li output_mask = output_mask >> 1; 18947ea5fda2SMin Li } 18957ea5fda2SMin Li qn = output_mask & 0x1; 18967ea5fda2SMin Li output_mask = output_mask >> 1; 18977ea5fda2SMin Li } else if (pll == 6) { 18987ea5fda2SMin Li qn = output_mask & 0x1; 18997ea5fda2SMin Li output_mask = output_mask >> 1; 19007ea5fda2SMin Li if (out11_mux) { 19017ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 19027ea5fda2SMin Li output_mask = output_mask >> 1; 19037ea5fda2SMin Li } 19047ea5fda2SMin Li } else if (pll == 7) { 19057ea5fda2SMin Li if (out11_mux == 0) { 19067ea5fda2SMin Li qn = output_mask & 0x1; 19077ea5fda2SMin Li output_mask = output_mask >> 1; 19087ea5fda2SMin Li } 19097ea5fda2SMin Li } 19107ea5fda2SMin Li 19117ea5fda2SMin Li if ((qn != 0) || (qn_plus_1 != 0)) 19127ea5fda2SMin Li err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn, 19137ea5fda2SMin Li qn_plus_1); 19147ea5fda2SMin Li 19157ea5fda2SMin Li if (err) 19167ea5fda2SMin Li return err; 19177ea5fda2SMin Li } 19187ea5fda2SMin Li 19197ea5fda2SMin Li return err; 19207ea5fda2SMin Li } 19217ea5fda2SMin Li 19223a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel) 19233a6ba7dcSVincent Cheng { 19243a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 19253a6ba7dcSVincent Cheng struct timespec64 ts = {0, 0}; 19263a6ba7dcSVincent Cheng u8 cfg; 19273a6ba7dcSVincent Cheng int err; 19283a6ba7dcSVincent Cheng 19293a6ba7dcSVincent Cheng /* 19303a6ba7dcSVincent Cheng * Start the TOD clock ticking. 19313a6ba7dcSVincent Cheng */ 19323a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19333a6ba7dcSVincent Cheng if (err) 19343a6ba7dcSVincent Cheng return err; 19353a6ba7dcSVincent Cheng 19363a6ba7dcSVincent Cheng cfg |= TOD_ENABLE; 19373a6ba7dcSVincent Cheng 19383a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19393a6ba7dcSVincent Cheng if (err) 19403a6ba7dcSVincent Cheng return err; 19413a6ba7dcSVincent Cheng 1942*da948233SMin Li if (idtcm->deprecated) 1943*da948233SMin Li return _idtcm_settime_deprecated(channel, &ts); 1944*da948233SMin Li else 1945*da948233SMin Li return _idtcm_settime(channel, &ts, 1946*da948233SMin Li SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 19473a6ba7dcSVincent Cheng } 19483a6ba7dcSVincent Cheng 1949*da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm) 19503a6ba7dcSVincent Cheng { 19513a6ba7dcSVincent Cheng u8 major; 19523a6ba7dcSVincent Cheng u8 minor; 19533a6ba7dcSVincent Cheng u8 hotfix; 19543a6ba7dcSVincent Cheng u16 product_id; 19553a6ba7dcSVincent Cheng u8 hw_rev_id; 19561ece2fbeSVincent Cheng u8 config_select; 19571ece2fbeSVincent Cheng char *fmt = "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d\n"; 19583a6ba7dcSVincent Cheng 19593a6ba7dcSVincent Cheng idtcm_read_major_release(idtcm, &major); 19603a6ba7dcSVincent Cheng idtcm_read_minor_release(idtcm, &minor); 19613a6ba7dcSVincent Cheng idtcm_read_hotfix_release(idtcm, &hotfix); 19623a6ba7dcSVincent Cheng 19633a6ba7dcSVincent Cheng idtcm_read_product_id(idtcm, &product_id); 19643a6ba7dcSVincent Cheng idtcm_read_hw_rev_id(idtcm, &hw_rev_id); 19653a6ba7dcSVincent Cheng 19661ece2fbeSVincent Cheng idtcm_read_otp_scsr_config_select(idtcm, &config_select); 19671ece2fbeSVincent Cheng 19687ea5fda2SMin Li snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u", 19697ea5fda2SMin Li major, minor, hotfix); 19707ea5fda2SMin Li 1971*da948233SMin Li if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) 1972*da948233SMin Li idtcm->deprecated = 0; 1973*da948233SMin Li else 1974*da948233SMin Li idtcm->deprecated = 1; 1975*da948233SMin Li 19761ece2fbeSVincent Cheng dev_info(&idtcm->client->dev, fmt, major, minor, hotfix, 19771ece2fbeSVincent Cheng product_id, hw_rev_id, config_select); 19783a6ba7dcSVincent Cheng } 19793a6ba7dcSVincent Cheng 19806485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = { 19813a6ba7dcSVincent Cheng .owner = THIS_MODULE, 19823a6ba7dcSVincent Cheng .max_adj = 244000, 19837ea5fda2SMin Li .n_per_out = 12, 1984425d2b1cSVincent Cheng .adjphase = &idtcm_adjphase, 19857ea5fda2SMin Li .adjfine = &idtcm_adjfine, 19863a6ba7dcSVincent Cheng .adjtime = &idtcm_adjtime, 19873a6ba7dcSVincent Cheng .gettime64 = &idtcm_gettime, 19883a6ba7dcSVincent Cheng .settime64 = &idtcm_settime, 19893a6ba7dcSVincent Cheng .enable = &idtcm_enable, 19903a6ba7dcSVincent Cheng }; 19913a6ba7dcSVincent Cheng 1992*da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = { 1993*da948233SMin Li .owner = THIS_MODULE, 1994*da948233SMin Li .max_adj = 244000, 1995*da948233SMin Li .n_per_out = 12, 1996*da948233SMin Li .adjphase = &idtcm_adjphase, 1997*da948233SMin Li .adjfine = &idtcm_adjfine, 1998*da948233SMin Li .adjtime = &idtcm_adjtime_deprecated, 1999*da948233SMin Li .gettime64 = &idtcm_gettime, 2000*da948233SMin Li .settime64 = &idtcm_settime_deprecated, 2001*da948233SMin Li .enable = &idtcm_enable, 2002*da948233SMin Li }; 2003*da948233SMin Li 20047ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel) 20053a6ba7dcSVincent Cheng { 20067ea5fda2SMin Li int err = 0; 20073a6ba7dcSVincent Cheng 20087ea5fda2SMin Li switch (channel->pll) { 20093a6ba7dcSVincent Cheng case 0: 20103a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_0; 20113a6ba7dcSVincent Cheng channel->dpll_n = DPLL_0; 20123a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_0; 20133a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_0; 20143a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_0; 20153a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0; 20163a6ba7dcSVincent Cheng break; 20173a6ba7dcSVincent Cheng case 1: 20183a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_1; 20193a6ba7dcSVincent Cheng channel->dpll_n = DPLL_1; 20203a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_1; 20213a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_1; 20223a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_1; 20233a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1; 20243a6ba7dcSVincent Cheng break; 20253a6ba7dcSVincent Cheng case 2: 20263a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_2; 20273a6ba7dcSVincent Cheng channel->dpll_n = DPLL_2; 20283a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_2; 20293a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_2; 20303a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_2; 20313a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2; 20323a6ba7dcSVincent Cheng break; 20333a6ba7dcSVincent Cheng case 3: 20343a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_3; 20353a6ba7dcSVincent Cheng channel->dpll_n = DPLL_3; 20363a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_3; 20373a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_3; 20383a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_3; 20393a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3; 20403a6ba7dcSVincent Cheng break; 20417ea5fda2SMin Li case 4: 20427ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_4; 20437ea5fda2SMin Li channel->dpll_n = DPLL_4; 20447ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_4; 20457ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_4; 20467ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_4; 20477ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4; 20487ea5fda2SMin Li break; 20497ea5fda2SMin Li case 5: 20507ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_5; 20517ea5fda2SMin Li channel->dpll_n = DPLL_5; 20527ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_5; 20537ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_5; 20547ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_5; 20557ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5; 20567ea5fda2SMin Li break; 20577ea5fda2SMin Li case 6: 20587ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_6; 20597ea5fda2SMin Li channel->dpll_n = DPLL_6; 20607ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_6; 20617ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_6; 20627ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_6; 20637ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6; 20647ea5fda2SMin Li break; 20657ea5fda2SMin Li case 7: 20667ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_7; 20677ea5fda2SMin Li channel->dpll_n = DPLL_7; 20687ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_7; 20697ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_7; 20707ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_7; 20717ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7; 20727ea5fda2SMin Li break; 20737ea5fda2SMin Li default: 20747ea5fda2SMin Li err = -EINVAL; 20757ea5fda2SMin Li } 20767ea5fda2SMin Li 20777ea5fda2SMin Li return err; 20787ea5fda2SMin Li } 20797ea5fda2SMin Li 20807ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) 20817ea5fda2SMin Li { 20827ea5fda2SMin Li struct idtcm_channel *channel; 20837ea5fda2SMin Li int err; 20847ea5fda2SMin Li 20857ea5fda2SMin Li if (!(index < MAX_TOD)) 20867ea5fda2SMin Li return -EINVAL; 20877ea5fda2SMin Li 20887ea5fda2SMin Li channel = &idtcm->channel[index]; 20897ea5fda2SMin Li 20907ea5fda2SMin Li /* Set pll addresses */ 20917ea5fda2SMin Li err = configure_channel_pll(channel); 20927ea5fda2SMin Li if (err) 20937ea5fda2SMin Li return err; 20947ea5fda2SMin Li 20957ea5fda2SMin Li /* Set tod addresses */ 20967ea5fda2SMin Li switch (index) { 20977ea5fda2SMin Li case 0: 20987ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_0; 20997ea5fda2SMin Li channel->tod_write = TOD_WRITE_0; 21007ea5fda2SMin Li channel->tod_n = TOD_0; 21017ea5fda2SMin Li break; 21027ea5fda2SMin Li case 1: 21037ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_1; 21047ea5fda2SMin Li channel->tod_write = TOD_WRITE_1; 21057ea5fda2SMin Li channel->tod_n = TOD_1; 21067ea5fda2SMin Li break; 21077ea5fda2SMin Li case 2: 21087ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_2; 21097ea5fda2SMin Li channel->tod_write = TOD_WRITE_2; 21107ea5fda2SMin Li channel->tod_n = TOD_2; 21117ea5fda2SMin Li break; 21127ea5fda2SMin Li case 3: 21137ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_3; 21147ea5fda2SMin Li channel->tod_write = TOD_WRITE_3; 21157ea5fda2SMin Li channel->tod_n = TOD_3; 21167ea5fda2SMin Li break; 21173a6ba7dcSVincent Cheng default: 21183a6ba7dcSVincent Cheng return -EINVAL; 21193a6ba7dcSVincent Cheng } 21203a6ba7dcSVincent Cheng 21213a6ba7dcSVincent Cheng channel->idtcm = idtcm; 21223a6ba7dcSVincent Cheng 2123*da948233SMin Li if (idtcm->deprecated) 2124*da948233SMin Li channel->caps = idtcm_caps_deprecated; 21257ea5fda2SMin Li else 21263a6ba7dcSVincent Cheng channel->caps = idtcm_caps; 21277ea5fda2SMin Li 21283a6ba7dcSVincent Cheng snprintf(channel->caps.name, sizeof(channel->caps.name), 21297ea5fda2SMin Li "IDT CM TOD%u", index); 21307ea5fda2SMin Li 2131*da948233SMin Li if (!idtcm->deprecated) { 21327ea5fda2SMin Li err = idtcm_enable_tod_sync(channel); 21337ea5fda2SMin Li if (err) { 21347ea5fda2SMin Li dev_err(&idtcm->client->dev, 21357ea5fda2SMin Li "Failed at line %d in func %s!\n", 21367ea5fda2SMin Li __LINE__, 21377ea5fda2SMin Li __func__); 21387ea5fda2SMin Li return err; 21397ea5fda2SMin Li } 21407ea5fda2SMin Li } 21413a6ba7dcSVincent Cheng 21427260d1c8SMin Li /* Sync pll mode with hardware */ 21437260d1c8SMin Li err = idtcm_get_pll_mode(channel, &channel->pll_mode); 21447ea5fda2SMin Li if (err) { 21457ea5fda2SMin Li dev_err(&idtcm->client->dev, 21467260d1c8SMin Li "Error: %s - Unable to read pll mode\n", __func__); 21473a6ba7dcSVincent Cheng return err; 21487ea5fda2SMin Li } 21493a6ba7dcSVincent Cheng 21503a6ba7dcSVincent Cheng err = idtcm_enable_tod(channel); 21517ea5fda2SMin Li if (err) { 21527ea5fda2SMin Li dev_err(&idtcm->client->dev, 21537ea5fda2SMin Li "Failed at line %d in func %s!\n", 21547ea5fda2SMin Li __LINE__, 21557ea5fda2SMin Li __func__); 21563a6ba7dcSVincent Cheng return err; 21577ea5fda2SMin Li } 21583a6ba7dcSVincent Cheng 21593a6ba7dcSVincent Cheng channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); 21603a6ba7dcSVincent Cheng 21613a6ba7dcSVincent Cheng if (IS_ERR(channel->ptp_clock)) { 21623a6ba7dcSVincent Cheng err = PTR_ERR(channel->ptp_clock); 21633a6ba7dcSVincent Cheng channel->ptp_clock = NULL; 21643a6ba7dcSVincent Cheng return err; 21653a6ba7dcSVincent Cheng } 21663a6ba7dcSVincent Cheng 21673a6ba7dcSVincent Cheng if (!channel->ptp_clock) 21683a6ba7dcSVincent Cheng return -ENOTSUPP; 21693a6ba7dcSVincent Cheng 21703a6ba7dcSVincent Cheng dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n", 21713a6ba7dcSVincent Cheng index, channel->ptp_clock->index); 21723a6ba7dcSVincent Cheng 21733a6ba7dcSVincent Cheng return 0; 21743a6ba7dcSVincent Cheng } 21753a6ba7dcSVincent Cheng 21763a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm) 21773a6ba7dcSVincent Cheng { 21783a6ba7dcSVincent Cheng u8 i; 21793a6ba7dcSVincent Cheng struct idtcm_channel *channel; 21803a6ba7dcSVincent Cheng 21817ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 21823a6ba7dcSVincent Cheng 21833a6ba7dcSVincent Cheng channel = &idtcm->channel[i]; 21843a6ba7dcSVincent Cheng 21853a6ba7dcSVincent Cheng if (channel->ptp_clock) 21863a6ba7dcSVincent Cheng ptp_clock_unregister(channel->ptp_clock); 21873a6ba7dcSVincent Cheng } 21883a6ba7dcSVincent Cheng } 21893a6ba7dcSVincent Cheng 21903a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm) 21913a6ba7dcSVincent Cheng { 21927ea5fda2SMin Li idtcm->tod_mask = DEFAULT_TOD_MASK; 21937ea5fda2SMin Li 21947ea5fda2SMin Li idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL; 21957ea5fda2SMin Li idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL; 21967ea5fda2SMin Li idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL; 21977ea5fda2SMin Li idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL; 21983a6ba7dcSVincent Cheng 21993a6ba7dcSVincent Cheng idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; 22003a6ba7dcSVincent Cheng idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; 22013a6ba7dcSVincent Cheng idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2; 22023a6ba7dcSVincent Cheng idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3; 22033a6ba7dcSVincent Cheng } 22043a6ba7dcSVincent Cheng 22053a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client, 22063a6ba7dcSVincent Cheng const struct i2c_device_id *id) 22073a6ba7dcSVincent Cheng { 22083a6ba7dcSVincent Cheng struct idtcm *idtcm; 22093a6ba7dcSVincent Cheng int err; 22103a6ba7dcSVincent Cheng u8 i; 22117ea5fda2SMin Li char *fmt = "Failed at %d in line %s with channel output %d!\n"; 22123a6ba7dcSVincent Cheng 22133a6ba7dcSVincent Cheng /* Unused for now */ 22143a6ba7dcSVincent Cheng (void)id; 22153a6ba7dcSVincent Cheng 22163a6ba7dcSVincent Cheng idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL); 22173a6ba7dcSVincent Cheng 22183a6ba7dcSVincent Cheng if (!idtcm) 22193a6ba7dcSVincent Cheng return -ENOMEM; 22203a6ba7dcSVincent Cheng 22213a6ba7dcSVincent Cheng idtcm->client = client; 22223a6ba7dcSVincent Cheng idtcm->page_offset = 0xff; 22233a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 22243a6ba7dcSVincent Cheng 22253a6ba7dcSVincent Cheng set_default_masks(idtcm); 22263a6ba7dcSVincent Cheng 22273a6ba7dcSVincent Cheng mutex_init(&idtcm->reg_lock); 22283a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 22293a6ba7dcSVincent Cheng 2230*da948233SMin Li idtcm_set_version_info(idtcm); 22313a6ba7dcSVincent Cheng 22323a6ba7dcSVincent Cheng err = idtcm_load_firmware(idtcm, &client->dev); 22333a6ba7dcSVincent Cheng 22343a6ba7dcSVincent Cheng if (err) 22353a6ba7dcSVincent Cheng dev_warn(&idtcm->client->dev, 22363a6ba7dcSVincent Cheng "loading firmware failed with %d\n", err); 22373a6ba7dcSVincent Cheng 2238251f4fe2SMin Li if (wait_for_boot_status_ready(idtcm)) 2239251f4fe2SMin Li dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0\n"); 2240251f4fe2SMin Li 22417ea5fda2SMin Li if (idtcm->tod_mask) { 22427ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 22437ea5fda2SMin Li if (idtcm->tod_mask & (1 << i)) { 22443a6ba7dcSVincent Cheng err = idtcm_enable_channel(idtcm, i); 22457ea5fda2SMin Li if (err) { 22467ea5fda2SMin Li dev_err(&idtcm->client->dev, 22477ea5fda2SMin Li fmt, 22487ea5fda2SMin Li __LINE__, 22497ea5fda2SMin Li __func__, 22507ea5fda2SMin Li i); 22513a6ba7dcSVincent Cheng break; 22523a6ba7dcSVincent Cheng } 22533a6ba7dcSVincent Cheng } 22547ea5fda2SMin Li } 22553a6ba7dcSVincent Cheng } else { 22563a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 22573a6ba7dcSVincent Cheng "no PLLs flagged as PHCs, nothing to do\n"); 22583a6ba7dcSVincent Cheng err = -ENODEV; 22593a6ba7dcSVincent Cheng } 22603a6ba7dcSVincent Cheng 22613a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 22623a6ba7dcSVincent Cheng 22633a6ba7dcSVincent Cheng if (err) { 22643a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22653a6ba7dcSVincent Cheng return err; 22663a6ba7dcSVincent Cheng } 22673a6ba7dcSVincent Cheng 22683a6ba7dcSVincent Cheng i2c_set_clientdata(client, idtcm); 22693a6ba7dcSVincent Cheng 22703a6ba7dcSVincent Cheng return 0; 22713a6ba7dcSVincent Cheng } 22723a6ba7dcSVincent Cheng 22733a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client) 22743a6ba7dcSVincent Cheng { 22753a6ba7dcSVincent Cheng struct idtcm *idtcm = i2c_get_clientdata(client); 22763a6ba7dcSVincent Cheng 22773a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22783a6ba7dcSVincent Cheng 22793a6ba7dcSVincent Cheng mutex_destroy(&idtcm->reg_lock); 22803a6ba7dcSVincent Cheng 22813a6ba7dcSVincent Cheng return 0; 22823a6ba7dcSVincent Cheng } 22833a6ba7dcSVincent Cheng 22843a6ba7dcSVincent Cheng #ifdef CONFIG_OF 22853a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = { 22863a6ba7dcSVincent Cheng { .compatible = "idt,8a34000" }, 22873a6ba7dcSVincent Cheng { .compatible = "idt,8a34001" }, 22883a6ba7dcSVincent Cheng { .compatible = "idt,8a34002" }, 22893a6ba7dcSVincent Cheng { .compatible = "idt,8a34003" }, 22903a6ba7dcSVincent Cheng { .compatible = "idt,8a34004" }, 22913a6ba7dcSVincent Cheng { .compatible = "idt,8a34005" }, 22923a6ba7dcSVincent Cheng { .compatible = "idt,8a34006" }, 22933a6ba7dcSVincent Cheng { .compatible = "idt,8a34007" }, 22943a6ba7dcSVincent Cheng { .compatible = "idt,8a34008" }, 22953a6ba7dcSVincent Cheng { .compatible = "idt,8a34009" }, 22963a6ba7dcSVincent Cheng { .compatible = "idt,8a34010" }, 22973a6ba7dcSVincent Cheng { .compatible = "idt,8a34011" }, 22983a6ba7dcSVincent Cheng { .compatible = "idt,8a34012" }, 22993a6ba7dcSVincent Cheng { .compatible = "idt,8a34013" }, 23003a6ba7dcSVincent Cheng { .compatible = "idt,8a34014" }, 23013a6ba7dcSVincent Cheng { .compatible = "idt,8a34015" }, 23023a6ba7dcSVincent Cheng { .compatible = "idt,8a34016" }, 23033a6ba7dcSVincent Cheng { .compatible = "idt,8a34017" }, 23043a6ba7dcSVincent Cheng { .compatible = "idt,8a34018" }, 23053a6ba7dcSVincent Cheng { .compatible = "idt,8a34019" }, 23063a6ba7dcSVincent Cheng { .compatible = "idt,8a34040" }, 23073a6ba7dcSVincent Cheng { .compatible = "idt,8a34041" }, 23083a6ba7dcSVincent Cheng { .compatible = "idt,8a34042" }, 23093a6ba7dcSVincent Cheng { .compatible = "idt,8a34043" }, 23103a6ba7dcSVincent Cheng { .compatible = "idt,8a34044" }, 23113a6ba7dcSVincent Cheng { .compatible = "idt,8a34045" }, 23123a6ba7dcSVincent Cheng { .compatible = "idt,8a34046" }, 23133a6ba7dcSVincent Cheng { .compatible = "idt,8a34047" }, 23143a6ba7dcSVincent Cheng { .compatible = "idt,8a34048" }, 23153a6ba7dcSVincent Cheng { .compatible = "idt,8a34049" }, 23163a6ba7dcSVincent Cheng {}, 23173a6ba7dcSVincent Cheng }; 23183a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id); 23193a6ba7dcSVincent Cheng #endif 23203a6ba7dcSVincent Cheng 23213a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = { 23223a6ba7dcSVincent Cheng { "8a34000" }, 23233a6ba7dcSVincent Cheng { "8a34001" }, 23243a6ba7dcSVincent Cheng { "8a34002" }, 23253a6ba7dcSVincent Cheng { "8a34003" }, 23263a6ba7dcSVincent Cheng { "8a34004" }, 23273a6ba7dcSVincent Cheng { "8a34005" }, 23283a6ba7dcSVincent Cheng { "8a34006" }, 23293a6ba7dcSVincent Cheng { "8a34007" }, 23303a6ba7dcSVincent Cheng { "8a34008" }, 23313a6ba7dcSVincent Cheng { "8a34009" }, 23323a6ba7dcSVincent Cheng { "8a34010" }, 23333a6ba7dcSVincent Cheng { "8a34011" }, 23343a6ba7dcSVincent Cheng { "8a34012" }, 23353a6ba7dcSVincent Cheng { "8a34013" }, 23363a6ba7dcSVincent Cheng { "8a34014" }, 23373a6ba7dcSVincent Cheng { "8a34015" }, 23383a6ba7dcSVincent Cheng { "8a34016" }, 23393a6ba7dcSVincent Cheng { "8a34017" }, 23403a6ba7dcSVincent Cheng { "8a34018" }, 23413a6ba7dcSVincent Cheng { "8a34019" }, 23423a6ba7dcSVincent Cheng { "8a34040" }, 23433a6ba7dcSVincent Cheng { "8a34041" }, 23443a6ba7dcSVincent Cheng { "8a34042" }, 23453a6ba7dcSVincent Cheng { "8a34043" }, 23463a6ba7dcSVincent Cheng { "8a34044" }, 23473a6ba7dcSVincent Cheng { "8a34045" }, 23483a6ba7dcSVincent Cheng { "8a34046" }, 23493a6ba7dcSVincent Cheng { "8a34047" }, 23503a6ba7dcSVincent Cheng { "8a34048" }, 23513a6ba7dcSVincent Cheng { "8a34049" }, 23523a6ba7dcSVincent Cheng {}, 23533a6ba7dcSVincent Cheng }; 23543a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id); 23553a6ba7dcSVincent Cheng 23563a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = { 23573a6ba7dcSVincent Cheng .driver = { 23583a6ba7dcSVincent Cheng .of_match_table = of_match_ptr(idtcm_dt_id), 23593a6ba7dcSVincent Cheng .name = "idtcm", 23603a6ba7dcSVincent Cheng }, 23613a6ba7dcSVincent Cheng .probe = idtcm_probe, 23623a6ba7dcSVincent Cheng .remove = idtcm_remove, 23633a6ba7dcSVincent Cheng .id_table = idtcm_i2c_id, 23643a6ba7dcSVincent Cheng }; 23653a6ba7dcSVincent Cheng 23663a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver); 2367