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; 1633a6ba7dcSVincent Cheng 1643a6ba7dcSVincent Cheng msg[0].addr = client->addr; 1653a6ba7dcSVincent Cheng msg[0].flags = 0; 1663a6ba7dcSVincent Cheng msg[0].len = 1; 1673a6ba7dcSVincent Cheng msg[0].buf = ®addr; 1683a6ba7dcSVincent Cheng 1693a6ba7dcSVincent Cheng msg[1].addr = client->addr; 170957ff427SMin Li msg[1].flags = I2C_M_RD; 1713a6ba7dcSVincent Cheng msg[1].len = count; 1723a6ba7dcSVincent Cheng msg[1].buf = buf; 1733a6ba7dcSVincent Cheng 1743a6ba7dcSVincent Cheng cnt = i2c_transfer(client->adapter, msg, 2); 1753a6ba7dcSVincent Cheng 1763a6ba7dcSVincent Cheng if (cnt < 0) { 1777ea5fda2SMin Li dev_err(&client->dev, 1781c49d3e9SVincent Cheng "i2c_transfer failed at %d in %s, at addr: %04x!", 1791c49d3e9SVincent Cheng __LINE__, __func__, regaddr); 1803a6ba7dcSVincent Cheng return cnt; 1813a6ba7dcSVincent Cheng } else if (cnt != 2) { 1823a6ba7dcSVincent Cheng dev_err(&client->dev, 1831c49d3e9SVincent Cheng "i2c_transfer sent only %d of %d messages", cnt, 2); 1843a6ba7dcSVincent Cheng return -EIO; 1853a6ba7dcSVincent Cheng } 1863a6ba7dcSVincent Cheng 1873a6ba7dcSVincent Cheng return 0; 1883a6ba7dcSVincent Cheng } 1893a6ba7dcSVincent Cheng 190957ff427SMin Li static int idtcm_xfer_write(struct idtcm *idtcm, 191957ff427SMin Li u8 regaddr, 192957ff427SMin Li u8 *buf, 193957ff427SMin Li u16 count) 194957ff427SMin Li { 195957ff427SMin Li struct i2c_client *client = idtcm->client; 196957ff427SMin Li /* we add 1 byte for device register */ 197957ff427SMin Li u8 msg[IDTCM_MAX_WRITE_COUNT + 1]; 198957ff427SMin Li int cnt; 199957ff427SMin Li 200957ff427SMin Li if (count > IDTCM_MAX_WRITE_COUNT) 201957ff427SMin Li return -EINVAL; 202957ff427SMin Li 203957ff427SMin Li msg[0] = regaddr; 204957ff427SMin Li memcpy(&msg[1], buf, count); 205957ff427SMin Li 206957ff427SMin Li cnt = i2c_master_send(client, msg, count + 1); 207957ff427SMin Li 208957ff427SMin Li if (cnt < 0) { 209957ff427SMin Li dev_err(&client->dev, 2101c49d3e9SVincent Cheng "i2c_master_send failed at %d in %s, at addr: %04x!", 2111c49d3e9SVincent Cheng __LINE__, __func__, regaddr); 212957ff427SMin Li return cnt; 213957ff427SMin Li } 214957ff427SMin Li 215957ff427SMin Li return 0; 216957ff427SMin Li } 217957ff427SMin Li 2183a6ba7dcSVincent Cheng static int idtcm_page_offset(struct idtcm *idtcm, u8 val) 2193a6ba7dcSVincent Cheng { 2203a6ba7dcSVincent Cheng u8 buf[4]; 2213a6ba7dcSVincent Cheng int err; 2223a6ba7dcSVincent Cheng 2233a6ba7dcSVincent Cheng if (idtcm->page_offset == val) 2243a6ba7dcSVincent Cheng return 0; 2253a6ba7dcSVincent Cheng 2263a6ba7dcSVincent Cheng buf[0] = 0x0; 2273a6ba7dcSVincent Cheng buf[1] = val; 2283a6ba7dcSVincent Cheng buf[2] = 0x10; 2293a6ba7dcSVincent Cheng buf[3] = 0x20; 2303a6ba7dcSVincent Cheng 231957ff427SMin Li err = idtcm_xfer_write(idtcm, PAGE_ADDR, buf, sizeof(buf)); 2327ea5fda2SMin Li if (err) { 2337ea5fda2SMin Li idtcm->page_offset = 0xff; 2341c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "failed to set page offset"); 2357ea5fda2SMin Li } else { 2363a6ba7dcSVincent Cheng idtcm->page_offset = val; 2377ea5fda2SMin Li } 2383a6ba7dcSVincent Cheng 2393a6ba7dcSVincent Cheng return err; 2403a6ba7dcSVincent Cheng } 2413a6ba7dcSVincent Cheng 2423a6ba7dcSVincent Cheng static int _idtcm_rdwr(struct idtcm *idtcm, 2433a6ba7dcSVincent Cheng u16 regaddr, 2443a6ba7dcSVincent Cheng u8 *buf, 2453a6ba7dcSVincent Cheng u16 count, 2463a6ba7dcSVincent Cheng bool write) 2473a6ba7dcSVincent Cheng { 2483a6ba7dcSVincent Cheng u8 hi; 2493a6ba7dcSVincent Cheng u8 lo; 2503a6ba7dcSVincent Cheng int err; 2513a6ba7dcSVincent Cheng 2523a6ba7dcSVincent Cheng hi = (regaddr >> 8) & 0xff; 2533a6ba7dcSVincent Cheng lo = regaddr & 0xff; 2543a6ba7dcSVincent Cheng 2553a6ba7dcSVincent Cheng err = idtcm_page_offset(idtcm, hi); 2563a6ba7dcSVincent Cheng if (err) 2573a6ba7dcSVincent Cheng return err; 258957ff427SMin Li 259957ff427SMin Li if (write) 260957ff427SMin Li return idtcm_xfer_write(idtcm, lo, buf, count); 261957ff427SMin Li 262957ff427SMin Li return idtcm_xfer_read(idtcm, lo, buf, count); 2633a6ba7dcSVincent Cheng } 2643a6ba7dcSVincent Cheng 2653a6ba7dcSVincent Cheng static int idtcm_read(struct idtcm *idtcm, 2663a6ba7dcSVincent Cheng u16 module, 2673a6ba7dcSVincent Cheng u16 regaddr, 2683a6ba7dcSVincent Cheng u8 *buf, 2693a6ba7dcSVincent Cheng u16 count) 2703a6ba7dcSVincent Cheng { 2713a6ba7dcSVincent Cheng return _idtcm_rdwr(idtcm, module + regaddr, buf, count, false); 2723a6ba7dcSVincent Cheng } 2733a6ba7dcSVincent Cheng 2743a6ba7dcSVincent Cheng static int idtcm_write(struct idtcm *idtcm, 2753a6ba7dcSVincent Cheng u16 module, 2763a6ba7dcSVincent Cheng u16 regaddr, 2773a6ba7dcSVincent Cheng u8 *buf, 2783a6ba7dcSVincent Cheng u16 count) 2793a6ba7dcSVincent Cheng { 2803a6ba7dcSVincent Cheng return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true); 2813a6ba7dcSVincent Cheng } 2823a6ba7dcSVincent Cheng 283251f4fe2SMin Li static int clear_boot_status(struct idtcm *idtcm) 284251f4fe2SMin Li { 285251f4fe2SMin Li u8 buf[4] = {0}; 286251f4fe2SMin Li 287*fde3b3a7SVincent Cheng return idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 288251f4fe2SMin Li } 289251f4fe2SMin Li 290251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status) 291251f4fe2SMin Li { 292251f4fe2SMin Li int err; 293251f4fe2SMin Li u8 buf[4] = {0}; 294251f4fe2SMin Li 295251f4fe2SMin Li err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 296251f4fe2SMin Li 297251f4fe2SMin Li *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; 298251f4fe2SMin Li 299251f4fe2SMin Li return err; 300251f4fe2SMin Li } 301251f4fe2SMin Li 302251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm) 303251f4fe2SMin Li { 304251f4fe2SMin Li u32 status = 0; 305251f4fe2SMin Li u8 i = 30; /* 30 * 100ms = 3s */ 306251f4fe2SMin Li int err; 307251f4fe2SMin Li 308251f4fe2SMin Li do { 309251f4fe2SMin Li err = read_boot_status(idtcm, &status); 310251f4fe2SMin Li if (err) 311251f4fe2SMin Li return err; 312251f4fe2SMin Li 313251f4fe2SMin Li if (status == 0xA0) 314251f4fe2SMin Li return 0; 315251f4fe2SMin Li 316251f4fe2SMin Li msleep(100); 317251f4fe2SMin Li i--; 318251f4fe2SMin Li 319251f4fe2SMin Li } while (i); 320251f4fe2SMin Li 3211c49d3e9SVincent Cheng dev_warn(&idtcm->client->dev, "%s timed out", __func__); 322251f4fe2SMin Li 323251f4fe2SMin Li return -EBUSY; 324251f4fe2SMin Li } 325251f4fe2SMin Li 326797d3186SVincent Cheng static int read_sys_apll_status(struct idtcm *idtcm, u8 *status) 327797d3186SVincent Cheng { 328797d3186SVincent Cheng return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status, 329797d3186SVincent Cheng sizeof(u8)); 330797d3186SVincent Cheng } 331797d3186SVincent Cheng 332797d3186SVincent Cheng static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status) 333797d3186SVincent Cheng { 334797d3186SVincent Cheng return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8)); 335797d3186SVincent Cheng } 336797d3186SVincent Cheng 337797d3186SVincent Cheng static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) 338797d3186SVincent Cheng { 339797d3186SVincent Cheng unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS); 340797d3186SVincent Cheng u8 apll = 0; 341797d3186SVincent Cheng u8 dpll = 0; 342797d3186SVincent Cheng int err; 343797d3186SVincent Cheng 344797d3186SVincent Cheng do { 345797d3186SVincent Cheng err = read_sys_apll_status(idtcm, &apll); 346797d3186SVincent Cheng if (err) 347797d3186SVincent Cheng return err; 348797d3186SVincent Cheng 349797d3186SVincent Cheng err = read_sys_dpll_status(idtcm, &dpll); 350797d3186SVincent Cheng if (err) 351797d3186SVincent Cheng return err; 352797d3186SVincent Cheng 353797d3186SVincent Cheng apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK; 354797d3186SVincent Cheng dpll &= DPLL_SYS_STATE_MASK; 355797d3186SVincent Cheng 356797d3186SVincent Cheng if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED && 357797d3186SVincent Cheng dpll == DPLL_STATE_LOCKED) { 358797d3186SVincent Cheng return 0; 359797d3186SVincent Cheng } else if (dpll == DPLL_STATE_FREERUN || 360797d3186SVincent Cheng dpll == DPLL_STATE_HOLDOVER || 361797d3186SVincent Cheng dpll == DPLL_STATE_OPEN_LOOP) { 362797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 363797d3186SVincent Cheng "No wait state: DPLL_SYS_STATE %d", dpll); 364797d3186SVincent Cheng return -EPERM; 365797d3186SVincent Cheng } 366797d3186SVincent Cheng 367797d3186SVincent Cheng msleep(LOCK_POLL_INTERVAL_MS); 368797d3186SVincent Cheng } while (time_is_after_jiffies(timeout)); 369797d3186SVincent Cheng 370797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 371797d3186SVincent Cheng "%d ms lock timeout: SYS APLL Loss Lock %d SYS DPLL state %d", 372797d3186SVincent Cheng LOCK_TIMEOUT_MS, apll, dpll); 373797d3186SVincent Cheng 374797d3186SVincent Cheng return -ETIME; 375797d3186SVincent Cheng } 376797d3186SVincent Cheng 377797d3186SVincent Cheng static void wait_for_chip_ready(struct idtcm *idtcm) 378797d3186SVincent Cheng { 379797d3186SVincent Cheng if (wait_for_boot_status_ready(idtcm)) 380797d3186SVincent Cheng dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0"); 381797d3186SVincent Cheng 382797d3186SVincent Cheng if (wait_for_sys_apll_dpll_lock(idtcm)) 383797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 384797d3186SVincent Cheng "Continuing while SYS APLL/DPLL is not locked"); 385797d3186SVincent Cheng } 386797d3186SVincent Cheng 3873a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel, 3883a6ba7dcSVincent Cheng struct timespec64 *ts) 3893a6ba7dcSVincent Cheng { 3903a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 3913a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 3927ea5fda2SMin Li u8 timeout = 10; 3933a6ba7dcSVincent Cheng u8 trigger; 3943a6ba7dcSVincent Cheng int err; 3953a6ba7dcSVincent Cheng 3963a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 3973a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 3983a6ba7dcSVincent Cheng if (err) 3993a6ba7dcSVincent Cheng return err; 4003a6ba7dcSVincent Cheng 4013a6ba7dcSVincent Cheng trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT); 4023a6ba7dcSVincent Cheng trigger |= (1 << TOD_READ_TRIGGER_SHIFT); 4037ea5fda2SMin Li trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */ 4043a6ba7dcSVincent Cheng 4053a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_read_primary, 4063a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 4077ea5fda2SMin Li if (err) 4087ea5fda2SMin Li return err; 4097ea5fda2SMin Li 4107ea5fda2SMin Li /* wait trigger to be 0 */ 4117ea5fda2SMin Li while (trigger & TOD_READ_TRIGGER_MASK) { 4127ea5fda2SMin Li if (idtcm->calculate_overhead_flag) 4137ea5fda2SMin Li idtcm->start_time = ktime_get_raw(); 4147ea5fda2SMin Li 4157ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_read_primary, 4167ea5fda2SMin Li TOD_READ_PRIMARY_CMD, &trigger, 4177ea5fda2SMin Li sizeof(trigger)); 4183a6ba7dcSVincent Cheng if (err) 4193a6ba7dcSVincent Cheng return err; 4203a6ba7dcSVincent Cheng 4217ea5fda2SMin Li if (--timeout == 0) 4227ea5fda2SMin Li return -EIO; 4237ea5fda2SMin Li } 4243a6ba7dcSVincent Cheng 4253a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 4263a6ba7dcSVincent Cheng TOD_READ_PRIMARY, buf, sizeof(buf)); 4273a6ba7dcSVincent Cheng if (err) 4283a6ba7dcSVincent Cheng return err; 4293a6ba7dcSVincent Cheng 4303a6ba7dcSVincent Cheng err = char_array_to_timespec(buf, sizeof(buf), ts); 4313a6ba7dcSVincent Cheng 4323a6ba7dcSVincent Cheng return err; 4333a6ba7dcSVincent Cheng } 4343a6ba7dcSVincent Cheng 4353a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm, 4363a6ba7dcSVincent Cheng u8 pll, 4373a6ba7dcSVincent Cheng u8 sync_src, 4383a6ba7dcSVincent Cheng u8 qn, 4393a6ba7dcSVincent Cheng u8 qn_plus_1) 4403a6ba7dcSVincent Cheng { 4413a6ba7dcSVincent Cheng int err; 4423a6ba7dcSVincent Cheng u8 val; 4433a6ba7dcSVincent Cheng u16 sync_ctrl0; 4443a6ba7dcSVincent Cheng u16 sync_ctrl1; 4457ea5fda2SMin Li u8 temp; 4463a6ba7dcSVincent Cheng 4473a6ba7dcSVincent Cheng if ((qn == 0) && (qn_plus_1 == 0)) 4483a6ba7dcSVincent Cheng return 0; 4493a6ba7dcSVincent Cheng 4503a6ba7dcSVincent Cheng switch (pll) { 4513a6ba7dcSVincent Cheng case 0: 4523a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0; 4533a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1; 4543a6ba7dcSVincent Cheng break; 4553a6ba7dcSVincent Cheng case 1: 4563a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0; 4573a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1; 4583a6ba7dcSVincent Cheng break; 4593a6ba7dcSVincent Cheng case 2: 4603a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0; 4613a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1; 4623a6ba7dcSVincent Cheng break; 4633a6ba7dcSVincent Cheng case 3: 4643a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0; 4653a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1; 4663a6ba7dcSVincent Cheng break; 4673a6ba7dcSVincent Cheng case 4: 4683a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0; 4693a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1; 4703a6ba7dcSVincent Cheng break; 4713a6ba7dcSVincent Cheng case 5: 4723a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0; 4733a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1; 4743a6ba7dcSVincent Cheng break; 4753a6ba7dcSVincent Cheng case 6: 4763a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0; 4773a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1; 4783a6ba7dcSVincent Cheng break; 4793a6ba7dcSVincent Cheng case 7: 4803a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0; 4813a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1; 4823a6ba7dcSVincent Cheng break; 4833a6ba7dcSVincent Cheng default: 4843a6ba7dcSVincent Cheng return -EINVAL; 4853a6ba7dcSVincent Cheng } 4863a6ba7dcSVincent Cheng 4873a6ba7dcSVincent Cheng val = SYNCTRL1_MASTER_SYNC_RST; 4883a6ba7dcSVincent Cheng 4893a6ba7dcSVincent Cheng /* Place master sync in reset */ 4903a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 4913a6ba7dcSVincent Cheng if (err) 4923a6ba7dcSVincent Cheng return err; 4933a6ba7dcSVincent Cheng 4943a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src)); 4953a6ba7dcSVincent Cheng if (err) 4963a6ba7dcSVincent Cheng return err; 4973a6ba7dcSVincent Cheng 4983a6ba7dcSVincent Cheng /* Set sync trigger mask */ 4993a6ba7dcSVincent Cheng val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG; 5003a6ba7dcSVincent Cheng 5013a6ba7dcSVincent Cheng if (qn) 5023a6ba7dcSVincent Cheng val |= SYNCTRL1_Q0_DIV_SYNC_TRIG; 5033a6ba7dcSVincent Cheng 5043a6ba7dcSVincent Cheng if (qn_plus_1) 5053a6ba7dcSVincent Cheng val |= SYNCTRL1_Q1_DIV_SYNC_TRIG; 5063a6ba7dcSVincent Cheng 5073a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 5083a6ba7dcSVincent Cheng if (err) 5093a6ba7dcSVincent Cheng return err; 5103a6ba7dcSVincent Cheng 5117ea5fda2SMin Li /* PLL5 can have OUT8 as second additional output. */ 5127ea5fda2SMin Li if ((pll == 5) && (qn_plus_1 != 0)) { 5137ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 5147ea5fda2SMin Li &temp, sizeof(temp)); 5157ea5fda2SMin Li if (err) 5167ea5fda2SMin Li return err; 5177ea5fda2SMin Li 5187ea5fda2SMin Li temp &= ~(Q9_TO_Q8_SYNC_TRIG); 5197ea5fda2SMin Li 5207ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 5217ea5fda2SMin Li &temp, sizeof(temp)); 5227ea5fda2SMin Li if (err) 5237ea5fda2SMin Li return err; 5247ea5fda2SMin Li 5257ea5fda2SMin Li temp |= Q9_TO_Q8_SYNC_TRIG; 5267ea5fda2SMin Li 5277ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 5287ea5fda2SMin Li &temp, sizeof(temp)); 5297ea5fda2SMin Li if (err) 5307ea5fda2SMin Li return err; 5317ea5fda2SMin Li } 5327ea5fda2SMin Li 5337ea5fda2SMin Li /* PLL6 can have OUT11 as second additional output. */ 5347ea5fda2SMin Li if ((pll == 6) && (qn_plus_1 != 0)) { 5357ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 5367ea5fda2SMin Li &temp, sizeof(temp)); 5377ea5fda2SMin Li if (err) 5387ea5fda2SMin Li return err; 5397ea5fda2SMin Li 5407ea5fda2SMin Li temp &= ~(Q10_TO_Q11_SYNC_TRIG); 5417ea5fda2SMin Li 5427ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 5437ea5fda2SMin Li &temp, sizeof(temp)); 5447ea5fda2SMin Li if (err) 5457ea5fda2SMin Li return err; 5467ea5fda2SMin Li 5477ea5fda2SMin Li temp |= Q10_TO_Q11_SYNC_TRIG; 5487ea5fda2SMin Li 5497ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 5507ea5fda2SMin Li &temp, sizeof(temp)); 5517ea5fda2SMin Li if (err) 5527ea5fda2SMin Li return err; 5537ea5fda2SMin Li } 5547ea5fda2SMin Li 5553a6ba7dcSVincent Cheng /* Place master sync out of reset */ 5563a6ba7dcSVincent Cheng val &= ~(SYNCTRL1_MASTER_SYNC_RST); 5573a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 5583a6ba7dcSVincent Cheng 5593a6ba7dcSVincent Cheng return err; 5603a6ba7dcSVincent Cheng } 5613a6ba7dcSVincent Cheng 5627ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src) 5637ea5fda2SMin Li { 5647ea5fda2SMin Li int err = 0; 5657ea5fda2SMin Li 5667ea5fda2SMin Li switch (tod_addr) { 5677ea5fda2SMin Li case TOD_0: 5687ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; 5697ea5fda2SMin Li break; 5707ea5fda2SMin Li case TOD_1: 5717ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; 5727ea5fda2SMin Li break; 5737ea5fda2SMin Li case TOD_2: 5747ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; 5757ea5fda2SMin Li break; 5767ea5fda2SMin Li case TOD_3: 5777ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; 5787ea5fda2SMin Li break; 5797ea5fda2SMin Li default: 5807ea5fda2SMin Li err = -EINVAL; 5817ea5fda2SMin Li } 5827ea5fda2SMin Li 5837ea5fda2SMin Li return err; 5847ea5fda2SMin Li } 5857ea5fda2SMin Li 5863a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel) 5873a6ba7dcSVincent Cheng { 5883a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 5893a6ba7dcSVincent Cheng u8 pll; 5903a6ba7dcSVincent Cheng u8 sync_src; 5913a6ba7dcSVincent Cheng u8 qn; 5923a6ba7dcSVincent Cheng u8 qn_plus_1; 5933a6ba7dcSVincent Cheng int err = 0; 5947ea5fda2SMin Li u8 out8_mux = 0; 5957ea5fda2SMin Li u8 out11_mux = 0; 5967ea5fda2SMin Li u8 temp; 5973a6ba7dcSVincent Cheng u16 output_mask = channel->output_mask; 5983a6ba7dcSVincent Cheng 5997ea5fda2SMin Li err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src); 6007ea5fda2SMin Li if (err) 6017ea5fda2SMin Li return err; 6027ea5fda2SMin Li 6037ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 6047ea5fda2SMin Li &temp, sizeof(temp)); 6057ea5fda2SMin Li if (err) 6067ea5fda2SMin Li return err; 6077ea5fda2SMin Li 6087ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 6097ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 6107ea5fda2SMin Li out8_mux = 1; 6117ea5fda2SMin Li 6127ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 6137ea5fda2SMin Li &temp, sizeof(temp)); 6147ea5fda2SMin Li if (err) 6157ea5fda2SMin Li return err; 6167ea5fda2SMin Li 6177ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 6187ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 6197ea5fda2SMin Li out11_mux = 1; 6203a6ba7dcSVincent Cheng 6213a6ba7dcSVincent Cheng for (pll = 0; pll < 8; pll++) { 6227ea5fda2SMin Li qn = 0; 6237ea5fda2SMin Li qn_plus_1 = 0; 6243a6ba7dcSVincent Cheng 6253a6ba7dcSVincent Cheng if (pll < 4) { 6263a6ba7dcSVincent Cheng /* First 4 pll has 2 outputs */ 6277ea5fda2SMin Li qn = output_mask & 0x1; 6287ea5fda2SMin Li output_mask = output_mask >> 1; 6293a6ba7dcSVincent Cheng qn_plus_1 = output_mask & 0x1; 6303a6ba7dcSVincent Cheng output_mask = output_mask >> 1; 6317ea5fda2SMin Li } else if (pll == 4) { 6327ea5fda2SMin Li if (out8_mux == 0) { 6337ea5fda2SMin Li qn = output_mask & 0x1; 6347ea5fda2SMin Li output_mask = output_mask >> 1; 6357ea5fda2SMin Li } 6367ea5fda2SMin Li } else if (pll == 5) { 6377ea5fda2SMin Li if (out8_mux) { 6387ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 6397ea5fda2SMin Li output_mask = output_mask >> 1; 6407ea5fda2SMin Li } 6417ea5fda2SMin Li qn = output_mask & 0x1; 6427ea5fda2SMin Li output_mask = output_mask >> 1; 6437ea5fda2SMin Li } else if (pll == 6) { 6447ea5fda2SMin Li qn = output_mask & 0x1; 6457ea5fda2SMin Li output_mask = output_mask >> 1; 6467ea5fda2SMin Li if (out11_mux) { 6477ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 6487ea5fda2SMin Li output_mask = output_mask >> 1; 6497ea5fda2SMin Li } 6507ea5fda2SMin Li } else if (pll == 7) { 6517ea5fda2SMin Li if (out11_mux == 0) { 6527ea5fda2SMin Li qn = output_mask & 0x1; 6537ea5fda2SMin Li output_mask = output_mask >> 1; 6547ea5fda2SMin Li } 6553a6ba7dcSVincent Cheng } 6563a6ba7dcSVincent Cheng 6573a6ba7dcSVincent Cheng if ((qn != 0) || (qn_plus_1 != 0)) 6583a6ba7dcSVincent Cheng err = _sync_pll_output(idtcm, pll, sync_src, qn, 6593a6ba7dcSVincent Cheng qn_plus_1); 6603a6ba7dcSVincent Cheng 6613a6ba7dcSVincent Cheng if (err) 6623a6ba7dcSVincent Cheng return err; 6633a6ba7dcSVincent Cheng } 6643a6ba7dcSVincent Cheng 6653a6ba7dcSVincent Cheng return err; 6663a6ba7dcSVincent Cheng } 6673a6ba7dcSVincent Cheng 6687ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel, 6693a6ba7dcSVincent Cheng struct timespec64 const *ts, 6703a6ba7dcSVincent Cheng enum hw_tod_write_trig_sel wr_trig) 6713a6ba7dcSVincent Cheng { 6723a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 6733a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 6743a6ba7dcSVincent Cheng u8 cmd; 6753a6ba7dcSVincent Cheng int err; 6763a6ba7dcSVincent Cheng struct timespec64 local_ts = *ts; 6773a6ba7dcSVincent Cheng s64 total_overhead_ns; 6783a6ba7dcSVincent Cheng 6793a6ba7dcSVincent Cheng /* Configure HW TOD write trigger. */ 6803a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6813a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6823a6ba7dcSVincent Cheng if (err) 6833a6ba7dcSVincent Cheng return err; 6843a6ba7dcSVincent Cheng 6853a6ba7dcSVincent Cheng cmd &= ~(0x0f); 6863a6ba7dcSVincent Cheng cmd |= wr_trig | 0x08; 6873a6ba7dcSVincent Cheng 6883a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6893a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6903a6ba7dcSVincent Cheng if (err) 6913a6ba7dcSVincent Cheng return err; 6923a6ba7dcSVincent Cheng 6933a6ba7dcSVincent Cheng if (wr_trig != HW_TOD_WR_TRIG_SEL_MSB) { 6943a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 6953a6ba7dcSVincent Cheng if (err) 6963a6ba7dcSVincent Cheng return err; 6973a6ba7dcSVincent Cheng 6983a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 6993a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 7003a6ba7dcSVincent Cheng if (err) 7013a6ba7dcSVincent Cheng return err; 7023a6ba7dcSVincent Cheng } 7033a6ba7dcSVincent Cheng 7043a6ba7dcSVincent Cheng /* ARM HW TOD write trigger. */ 7053a6ba7dcSVincent Cheng cmd &= ~(0x08); 7063a6ba7dcSVincent Cheng 7073a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 7083a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 7093a6ba7dcSVincent Cheng 7103a6ba7dcSVincent Cheng if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) { 7113a6ba7dcSVincent Cheng if (idtcm->calculate_overhead_flag) { 7121ece2fbeSVincent Cheng /* Assumption: I2C @ 400KHz */ 7137260d1c8SMin Li ktime_t diff = ktime_sub(ktime_get_raw(), 7147260d1c8SMin Li idtcm->start_time); 7157260d1c8SMin Li total_overhead_ns = ktime_to_ns(diff) 7163a6ba7dcSVincent Cheng + idtcm->tod_write_overhead_ns 7173a6ba7dcSVincent Cheng + SETTIME_CORRECTION; 7183a6ba7dcSVincent Cheng 7193a6ba7dcSVincent Cheng timespec64_add_ns(&local_ts, total_overhead_ns); 7203a6ba7dcSVincent Cheng 7213a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 7223a6ba7dcSVincent Cheng } 7233a6ba7dcSVincent Cheng 7243a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 7253a6ba7dcSVincent Cheng if (err) 7263a6ba7dcSVincent Cheng return err; 7273a6ba7dcSVincent Cheng 7283a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 7293a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 7303a6ba7dcSVincent Cheng } 7313a6ba7dcSVincent Cheng 7323a6ba7dcSVincent Cheng return err; 7333a6ba7dcSVincent Cheng } 7343a6ba7dcSVincent Cheng 7357ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, 7367ea5fda2SMin Li struct timespec64 const *ts, 7377ea5fda2SMin Li enum scsr_tod_write_trig_sel wr_trig, 7387ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 7397ea5fda2SMin Li { 7407ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 7417ea5fda2SMin Li unsigned char buf[TOD_BYTE_COUNT], cmd; 7427ea5fda2SMin Li struct timespec64 local_ts = *ts; 7437ea5fda2SMin Li int err, count = 0; 7447ea5fda2SMin Li 7457ea5fda2SMin Li timespec64_add_ns(&local_ts, SETTIME_CORRECTION); 7467ea5fda2SMin Li 7477ea5fda2SMin Li err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 7487ea5fda2SMin Li if (err) 7497ea5fda2SMin Li return err; 7507ea5fda2SMin Li 7517ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE, 7527ea5fda2SMin Li buf, sizeof(buf)); 7537ea5fda2SMin Li if (err) 7547ea5fda2SMin Li return err; 7557ea5fda2SMin Li 7567ea5fda2SMin Li /* Trigger the write operation. */ 7577ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7587ea5fda2SMin Li &cmd, sizeof(cmd)); 7597ea5fda2SMin Li if (err) 7607ea5fda2SMin Li return err; 7617ea5fda2SMin Li 7627ea5fda2SMin Li cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT); 7637ea5fda2SMin Li cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT); 7647ea5fda2SMin Li cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT); 7657ea5fda2SMin Li cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT); 7667ea5fda2SMin Li 7677ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD, 7687ea5fda2SMin Li &cmd, sizeof(cmd)); 7697ea5fda2SMin Li if (err) 7707ea5fda2SMin Li return err; 7717ea5fda2SMin Li 7727ea5fda2SMin Li /* Wait for the operation to complete. */ 7737ea5fda2SMin Li while (1) { 7747ea5fda2SMin Li /* pps trigger takes up to 1 sec to complete */ 7757ea5fda2SMin Li if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS) 7767ea5fda2SMin Li msleep(50); 7777ea5fda2SMin Li 7787ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7797ea5fda2SMin Li &cmd, sizeof(cmd)); 7807ea5fda2SMin Li if (err) 7817ea5fda2SMin Li return err; 7827ea5fda2SMin Li 783251f4fe2SMin Li if ((cmd & TOD_WRITE_SELECTION_MASK) == 0) 7847ea5fda2SMin Li break; 7857ea5fda2SMin Li 7867ea5fda2SMin Li if (++count > 20) { 7877ea5fda2SMin Li dev_err(&idtcm->client->dev, 7881c49d3e9SVincent Cheng "Timed out waiting for the write counter"); 7897ea5fda2SMin Li return -EIO; 7907ea5fda2SMin Li } 7917ea5fda2SMin Li } 7927ea5fda2SMin Li 7937ea5fda2SMin Li return 0; 7947ea5fda2SMin Li } 7957ea5fda2SMin Li 7967260d1c8SMin Li static int get_output_base_addr(u8 outn) 7977260d1c8SMin Li { 7987260d1c8SMin Li int base; 7997260d1c8SMin Li 8007260d1c8SMin Li switch (outn) { 8017260d1c8SMin Li case 0: 8027260d1c8SMin Li base = OUTPUT_0; 8037260d1c8SMin Li break; 8047260d1c8SMin Li case 1: 8057260d1c8SMin Li base = OUTPUT_1; 8067260d1c8SMin Li break; 8077260d1c8SMin Li case 2: 8087260d1c8SMin Li base = OUTPUT_2; 8097260d1c8SMin Li break; 8107260d1c8SMin Li case 3: 8117260d1c8SMin Li base = OUTPUT_3; 8127260d1c8SMin Li break; 8137260d1c8SMin Li case 4: 8147260d1c8SMin Li base = OUTPUT_4; 8157260d1c8SMin Li break; 8167260d1c8SMin Li case 5: 8177260d1c8SMin Li base = OUTPUT_5; 8187260d1c8SMin Li break; 8197260d1c8SMin Li case 6: 8207260d1c8SMin Li base = OUTPUT_6; 8217260d1c8SMin Li break; 8227260d1c8SMin Li case 7: 8237260d1c8SMin Li base = OUTPUT_7; 8247260d1c8SMin Li break; 8257260d1c8SMin Li case 8: 8267260d1c8SMin Li base = OUTPUT_8; 8277260d1c8SMin Li break; 8287260d1c8SMin Li case 9: 8297260d1c8SMin Li base = OUTPUT_9; 8307260d1c8SMin Li break; 8317260d1c8SMin Li case 10: 8327260d1c8SMin Li base = OUTPUT_10; 8337260d1c8SMin Li break; 8347260d1c8SMin Li case 11: 8357260d1c8SMin Li base = OUTPUT_11; 8367260d1c8SMin Li break; 8377260d1c8SMin Li default: 8387260d1c8SMin Li base = -EINVAL; 8397260d1c8SMin Li } 8407260d1c8SMin Li 8417260d1c8SMin Li return base; 8427260d1c8SMin Li } 8437260d1c8SMin Li 844da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel, 845251f4fe2SMin Li struct timespec64 const *ts) 8463a6ba7dcSVincent Cheng { 8473a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8483a6ba7dcSVincent Cheng int err; 8493a6ba7dcSVincent Cheng 850251f4fe2SMin Li err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB); 8517ea5fda2SMin Li if (err) { 8527ea5fda2SMin Li dev_err(&idtcm->client->dev, 8531c49d3e9SVincent Cheng "%s: Set HW ToD failed", __func__); 8543a6ba7dcSVincent Cheng return err; 8557ea5fda2SMin Li } 8563a6ba7dcSVincent Cheng 8577ea5fda2SMin Li return idtcm_sync_pps_output(channel); 8587ea5fda2SMin Li } 8593a6ba7dcSVincent Cheng 860da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel, 8617ea5fda2SMin Li struct timespec64 const *ts, 8627ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 8637ea5fda2SMin Li { 8647ea5fda2SMin Li return _idtcm_set_dpll_scsr_tod(channel, ts, 8657ea5fda2SMin Li SCSR_TOD_WR_TRIG_SEL_IMMEDIATE, 8667ea5fda2SMin Li wr_type); 8673a6ba7dcSVincent Cheng } 8683a6ba7dcSVincent Cheng 8693a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel, 8703a6ba7dcSVincent Cheng s32 offset_ns) 8713a6ba7dcSVincent Cheng { 8723a6ba7dcSVincent Cheng int err; 8733a6ba7dcSVincent Cheng int i; 8743a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8753a6ba7dcSVincent Cheng u8 buf[4]; 8763a6ba7dcSVincent Cheng 8773a6ba7dcSVincent Cheng for (i = 0; i < 4; i++) { 8783a6ba7dcSVincent Cheng buf[i] = 0xff & (offset_ns); 8793a6ba7dcSVincent Cheng offset_ns >>= 8; 8803a6ba7dcSVincent Cheng } 8813a6ba7dcSVincent Cheng 8823a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET, 8833a6ba7dcSVincent Cheng buf, sizeof(buf)); 8843a6ba7dcSVincent Cheng 8853a6ba7dcSVincent Cheng return err; 8863a6ba7dcSVincent Cheng } 8873a6ba7dcSVincent Cheng 8883a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel, 8893a6ba7dcSVincent Cheng u32 max_ffo_ppb) 8903a6ba7dcSVincent Cheng { 8913a6ba7dcSVincent Cheng int err; 8923a6ba7dcSVincent Cheng u8 i; 8933a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8943a6ba7dcSVincent Cheng u8 buf[3]; 8953a6ba7dcSVincent Cheng 8963a6ba7dcSVincent Cheng if (max_ffo_ppb & 0xff000000) 8973a6ba7dcSVincent Cheng max_ffo_ppb = 0; 8983a6ba7dcSVincent Cheng 8993a6ba7dcSVincent Cheng for (i = 0; i < 3; i++) { 9003a6ba7dcSVincent Cheng buf[i] = 0xff & (max_ffo_ppb); 9013a6ba7dcSVincent Cheng max_ffo_ppb >>= 8; 9023a6ba7dcSVincent Cheng } 9033a6ba7dcSVincent Cheng 9043a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 9053a6ba7dcSVincent Cheng PULL_IN_SLOPE_LIMIT, buf, sizeof(buf)); 9063a6ba7dcSVincent Cheng 9073a6ba7dcSVincent Cheng return err; 9083a6ba7dcSVincent Cheng } 9093a6ba7dcSVincent Cheng 9103a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel) 9113a6ba7dcSVincent Cheng { 9123a6ba7dcSVincent Cheng int err; 9133a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 9143a6ba7dcSVincent Cheng u8 buf; 9153a6ba7dcSVincent Cheng 9163a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL, 9173a6ba7dcSVincent Cheng &buf, sizeof(buf)); 9183a6ba7dcSVincent Cheng if (err) 9193a6ba7dcSVincent Cheng return err; 9203a6ba7dcSVincent Cheng 9213a6ba7dcSVincent Cheng if (buf == 0) { 9223a6ba7dcSVincent Cheng buf = 0x01; 9233a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 9243a6ba7dcSVincent Cheng PULL_IN_CTRL, &buf, sizeof(buf)); 9253a6ba7dcSVincent Cheng } else { 9263a6ba7dcSVincent Cheng err = -EBUSY; 9273a6ba7dcSVincent Cheng } 9283a6ba7dcSVincent Cheng 9293a6ba7dcSVincent Cheng return err; 9303a6ba7dcSVincent Cheng } 9313a6ba7dcSVincent Cheng 9323a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel, 9333a6ba7dcSVincent Cheng s32 offset_ns, 9343a6ba7dcSVincent Cheng u32 max_ffo_ppb) 9353a6ba7dcSVincent Cheng { 9363a6ba7dcSVincent Cheng int err; 9373a6ba7dcSVincent Cheng 9383a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_offset(channel, -offset_ns); 9393a6ba7dcSVincent Cheng if (err) 9403a6ba7dcSVincent Cheng return err; 9413a6ba7dcSVincent Cheng 9423a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb); 9433a6ba7dcSVincent Cheng if (err) 9443a6ba7dcSVincent Cheng return err; 9453a6ba7dcSVincent Cheng 9463a6ba7dcSVincent Cheng err = idtcm_start_phase_pull_in(channel); 9473a6ba7dcSVincent Cheng 9483a6ba7dcSVincent Cheng return err; 9493a6ba7dcSVincent Cheng } 9503a6ba7dcSVincent Cheng 9517ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel) 9527ea5fda2SMin Li { 9537ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 9547ea5fda2SMin Li s64 current_ns = 0; 9557ea5fda2SMin Li s64 lowest_ns = 0; 9567ea5fda2SMin Li int err; 9577ea5fda2SMin Li u8 i; 9587ea5fda2SMin Li ktime_t start; 9597ea5fda2SMin Li ktime_t stop; 9607260d1c8SMin Li ktime_t diff; 9617ea5fda2SMin Li 9627ea5fda2SMin Li char buf[TOD_BYTE_COUNT] = {0}; 9637ea5fda2SMin Li 9647ea5fda2SMin Li /* Set page offset */ 9657ea5fda2SMin Li idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0, 9667ea5fda2SMin Li buf, sizeof(buf)); 9677ea5fda2SMin Li 9687ea5fda2SMin Li for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) { 9697ea5fda2SMin Li start = ktime_get_raw(); 9707ea5fda2SMin Li 9717ea5fda2SMin Li err = idtcm_write(idtcm, channel->hw_dpll_n, 9727ea5fda2SMin Li HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 9737ea5fda2SMin Li if (err) 9747ea5fda2SMin Li return err; 9757ea5fda2SMin Li 9767ea5fda2SMin Li stop = ktime_get_raw(); 9777ea5fda2SMin Li 9787260d1c8SMin Li diff = ktime_sub(stop, start); 9797260d1c8SMin Li 9807260d1c8SMin Li current_ns = ktime_to_ns(diff); 9817ea5fda2SMin Li 9827ea5fda2SMin Li if (i == 0) { 9837ea5fda2SMin Li lowest_ns = current_ns; 9847ea5fda2SMin Li } else { 9857ea5fda2SMin Li if (current_ns < lowest_ns) 9867ea5fda2SMin Li lowest_ns = current_ns; 9877ea5fda2SMin Li } 9887ea5fda2SMin Li } 9897ea5fda2SMin Li 9907ea5fda2SMin Li idtcm->tod_write_overhead_ns = lowest_ns; 9917ea5fda2SMin Li 9927ea5fda2SMin Li return err; 9937ea5fda2SMin Li } 9947ea5fda2SMin Li 995da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) 9963a6ba7dcSVincent Cheng { 9973a6ba7dcSVincent Cheng int err; 9983a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 9993a6ba7dcSVincent Cheng struct timespec64 ts; 10003a6ba7dcSVincent Cheng s64 now; 10013a6ba7dcSVincent Cheng 1002da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) { 10033a6ba7dcSVincent Cheng err = idtcm_do_phase_pull_in(channel, delta, 0); 10043a6ba7dcSVincent Cheng } else { 10053a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 1; 10063a6ba7dcSVincent Cheng 10077ea5fda2SMin Li err = set_tod_write_overhead(channel); 10087ea5fda2SMin Li if (err) 10097ea5fda2SMin Li return err; 10107ea5fda2SMin Li 10113a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, &ts); 10123a6ba7dcSVincent Cheng if (err) 10133a6ba7dcSVincent Cheng return err; 10143a6ba7dcSVincent Cheng 10153a6ba7dcSVincent Cheng now = timespec64_to_ns(&ts); 10163a6ba7dcSVincent Cheng now += delta; 10173a6ba7dcSVincent Cheng 10183a6ba7dcSVincent Cheng ts = ns_to_timespec64(now); 10193a6ba7dcSVincent Cheng 1020da948233SMin Li err = _idtcm_settime_deprecated(channel, &ts); 10213a6ba7dcSVincent Cheng } 10223a6ba7dcSVincent Cheng 10233a6ba7dcSVincent Cheng return err; 10243a6ba7dcSVincent Cheng } 10253a6ba7dcSVincent Cheng 10263a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm) 10273a6ba7dcSVincent Cheng { 10283a6ba7dcSVincent Cheng u8 byte = SM_RESET_CMD; 1029251f4fe2SMin Li u32 status = 0; 1030251f4fe2SMin Li int err; 1031251f4fe2SMin Li u8 i; 1032251f4fe2SMin Li 1033251f4fe2SMin Li clear_boot_status(idtcm); 10343a6ba7dcSVincent Cheng 10353a6ba7dcSVincent Cheng err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte)); 10363a6ba7dcSVincent Cheng 1037251f4fe2SMin Li if (!err) { 1038251f4fe2SMin Li for (i = 0; i < 30; i++) { 1039251f4fe2SMin Li msleep_interruptible(100); 1040251f4fe2SMin Li read_boot_status(idtcm, &status); 1041251f4fe2SMin Li 1042251f4fe2SMin Li if (status == 0xA0) { 1043251f4fe2SMin Li dev_dbg(&idtcm->client->dev, 10441c49d3e9SVincent Cheng "SM_RESET completed in %d ms", i * 100); 1045251f4fe2SMin Li break; 1046251f4fe2SMin Li } 1047251f4fe2SMin Li } 1048251f4fe2SMin Li 1049251f4fe2SMin Li if (!status) 10501c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, 10511c49d3e9SVincent Cheng "Timed out waiting for CM_RESET to complete"); 1052251f4fe2SMin Li } 10533a6ba7dcSVincent Cheng 10543a6ba7dcSVincent Cheng return err; 10553a6ba7dcSVincent Cheng } 10563a6ba7dcSVincent Cheng 10573a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id) 10583a6ba7dcSVincent Cheng { 10591ece2fbeSVincent Cheng return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8)); 10603a6ba7dcSVincent Cheng } 10613a6ba7dcSVincent Cheng 10623a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id) 10633a6ba7dcSVincent Cheng { 10643a6ba7dcSVincent Cheng int err; 10653a6ba7dcSVincent Cheng u8 buf[2] = {0}; 10663a6ba7dcSVincent Cheng 10673a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf)); 10683a6ba7dcSVincent Cheng 10693a6ba7dcSVincent Cheng *product_id = (buf[1] << 8) | buf[0]; 10703a6ba7dcSVincent Cheng 10713a6ba7dcSVincent Cheng return err; 10723a6ba7dcSVincent Cheng } 10733a6ba7dcSVincent Cheng 10743a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major) 10753a6ba7dcSVincent Cheng { 10763a6ba7dcSVincent Cheng int err; 10773a6ba7dcSVincent Cheng u8 buf = 0; 10783a6ba7dcSVincent Cheng 10793a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf)); 10803a6ba7dcSVincent Cheng 10813a6ba7dcSVincent Cheng *major = buf >> 1; 10823a6ba7dcSVincent Cheng 10833a6ba7dcSVincent Cheng return err; 10843a6ba7dcSVincent Cheng } 10853a6ba7dcSVincent Cheng 10863a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor) 10873a6ba7dcSVincent Cheng { 10883a6ba7dcSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8)); 10893a6ba7dcSVincent Cheng } 10903a6ba7dcSVincent Cheng 10913a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix) 10923a6ba7dcSVincent Cheng { 10933a6ba7dcSVincent Cheng return idtcm_read(idtcm, 10943a6ba7dcSVincent Cheng GENERAL_STATUS, 10953a6ba7dcSVincent Cheng HOTFIX_REL, 10963a6ba7dcSVincent Cheng hotfix, 10973a6ba7dcSVincent Cheng sizeof(u8)); 10983a6ba7dcSVincent Cheng } 10993a6ba7dcSVincent Cheng 11001ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm, 11011ece2fbeSVincent Cheng u8 *config_select) 11023a6ba7dcSVincent Cheng { 11031ece2fbeSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT, 11041ece2fbeSVincent Cheng config_select, sizeof(u8)); 11053a6ba7dcSVincent Cheng } 11063a6ba7dcSVincent Cheng 11073a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val) 11083a6ba7dcSVincent Cheng { 11093a6ba7dcSVincent Cheng int err = 0; 11103a6ba7dcSVincent Cheng 11113a6ba7dcSVincent Cheng switch (addr) { 11127ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR: 11133a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[0].output_mask, val); 11143a6ba7dcSVincent Cheng break; 11157ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR + 1: 11163a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[0].output_mask, val); 11173a6ba7dcSVincent Cheng break; 11187ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR: 11193a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[1].output_mask, val); 11203a6ba7dcSVincent Cheng break; 11217ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR + 1: 11223a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[1].output_mask, val); 11233a6ba7dcSVincent Cheng break; 11247ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR: 11253a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[2].output_mask, val); 11263a6ba7dcSVincent Cheng break; 11277ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR + 1: 11283a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[2].output_mask, val); 11293a6ba7dcSVincent Cheng break; 11307ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR: 11313a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[3].output_mask, val); 11323a6ba7dcSVincent Cheng break; 11337ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR + 1: 11343a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[3].output_mask, val); 11353a6ba7dcSVincent Cheng break; 11363a6ba7dcSVincent Cheng default: 11377ea5fda2SMin Li err = -EFAULT; /* Bad address */; 11383a6ba7dcSVincent Cheng break; 11393a6ba7dcSVincent Cheng } 11403a6ba7dcSVincent Cheng 11413a6ba7dcSVincent Cheng return err; 11423a6ba7dcSVincent Cheng } 11433a6ba7dcSVincent Cheng 11447ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll) 11457ea5fda2SMin Li { 11467ea5fda2SMin Li if (index >= MAX_TOD) { 11471c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "ToD%d not supported", index); 11487ea5fda2SMin Li return -EINVAL; 11497ea5fda2SMin Li } 11507ea5fda2SMin Li 11517ea5fda2SMin Li if (pll >= MAX_PLL) { 11521c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Pll%d not supported", pll); 11537ea5fda2SMin Li return -EINVAL; 11547ea5fda2SMin Li } 11557ea5fda2SMin Li 11567ea5fda2SMin Li idtcm->channel[index].pll = pll; 11577ea5fda2SMin Li 11587ea5fda2SMin Li return 0; 11597ea5fda2SMin Li } 11607ea5fda2SMin Li 11613a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm, 11623a6ba7dcSVincent Cheng u16 regaddr, 11633a6ba7dcSVincent Cheng u8 val) 11643a6ba7dcSVincent Cheng { 11653a6ba7dcSVincent Cheng int err = 0; 11663a6ba7dcSVincent Cheng 11677ea5fda2SMin Li switch (regaddr) { 11687ea5fda2SMin Li case TOD_MASK_ADDR: 11697ea5fda2SMin Li if ((val & 0xf0) || !(val & 0x0f)) { 11701c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val); 11717ea5fda2SMin Li err = -EINVAL; 11727ea5fda2SMin Li } else { 11737ea5fda2SMin Li idtcm->tod_mask = val; 11747ea5fda2SMin Li } 11757ea5fda2SMin Li break; 11767ea5fda2SMin Li case TOD0_PTP_PLL_ADDR: 11777ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 0, val); 11787ea5fda2SMin Li break; 11797ea5fda2SMin Li case TOD1_PTP_PLL_ADDR: 11807ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 1, val); 11817ea5fda2SMin Li break; 11827ea5fda2SMin Li case TOD2_PTP_PLL_ADDR: 11837ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 2, val); 11847ea5fda2SMin Li break; 11857ea5fda2SMin Li case TOD3_PTP_PLL_ADDR: 11867ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 3, val); 11877ea5fda2SMin Li break; 11887ea5fda2SMin Li default: 11897ea5fda2SMin Li err = set_pll_output_mask(idtcm, regaddr, val); 11907ea5fda2SMin Li break; 11913a6ba7dcSVincent Cheng } 11923a6ba7dcSVincent Cheng 11933a6ba7dcSVincent Cheng return err; 11943a6ba7dcSVincent Cheng } 11953a6ba7dcSVincent Cheng 11967ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm) 11973a6ba7dcSVincent Cheng { 11983a6ba7dcSVincent Cheng u8 i; 11993a6ba7dcSVincent Cheng u8 mask; 12003a6ba7dcSVincent Cheng 12011c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask); 12023a6ba7dcSVincent Cheng 12037ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 12043a6ba7dcSVincent Cheng mask = 1 << i; 12053a6ba7dcSVincent Cheng 12067ea5fda2SMin Li if (mask & idtcm->tod_mask) 12073a6ba7dcSVincent Cheng dev_dbg(&idtcm->client->dev, 12081c49d3e9SVincent Cheng "TOD%d pll = %d output_mask = 0x%04x", 12097ea5fda2SMin Li i, idtcm->channel[i].pll, 12107ea5fda2SMin Li idtcm->channel[i].output_mask); 12113a6ba7dcSVincent Cheng } 12123a6ba7dcSVincent Cheng } 12133a6ba7dcSVincent Cheng 12143a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm, 12153a6ba7dcSVincent Cheng struct device *dev) 12163a6ba7dcSVincent Cheng { 12177ea5fda2SMin Li char fname[128] = FW_FILENAME; 12183a6ba7dcSVincent Cheng const struct firmware *fw; 12193a6ba7dcSVincent Cheng struct idtcm_fwrc *rec; 12203a6ba7dcSVincent Cheng u32 regaddr; 12213a6ba7dcSVincent Cheng int err; 12223a6ba7dcSVincent Cheng s32 len; 12233a6ba7dcSVincent Cheng u8 val; 12243a6ba7dcSVincent Cheng u8 loaddr; 12253a6ba7dcSVincent Cheng 12267ea5fda2SMin Li if (firmware) /* module parameter */ 12277ea5fda2SMin Li snprintf(fname, sizeof(fname), "%s", firmware); 12283a6ba7dcSVincent Cheng 12291c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname); 12303a6ba7dcSVincent Cheng 12317ea5fda2SMin Li err = request_firmware(&fw, fname, dev); 12327ea5fda2SMin Li if (err) { 12337ea5fda2SMin Li dev_err(&idtcm->client->dev, 12341c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 12353a6ba7dcSVincent Cheng return err; 12367ea5fda2SMin Li } 12373a6ba7dcSVincent Cheng 12381c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size); 12393a6ba7dcSVincent Cheng 12403a6ba7dcSVincent Cheng rec = (struct idtcm_fwrc *) fw->data; 12413a6ba7dcSVincent Cheng 1242251f4fe2SMin Li if (contains_full_configuration(fw)) 12433a6ba7dcSVincent Cheng idtcm_state_machine_reset(idtcm); 12443a6ba7dcSVincent Cheng 12453a6ba7dcSVincent Cheng for (len = fw->size; len > 0; len -= sizeof(*rec)) { 12463a6ba7dcSVincent Cheng if (rec->reserved) { 12473a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 12481c49d3e9SVincent Cheng "bad firmware, reserved field non-zero"); 12493a6ba7dcSVincent Cheng err = -EINVAL; 12503a6ba7dcSVincent Cheng } else { 12513a6ba7dcSVincent Cheng regaddr = rec->hiaddr << 8; 12523a6ba7dcSVincent Cheng regaddr |= rec->loaddr; 12533a6ba7dcSVincent Cheng 12543a6ba7dcSVincent Cheng val = rec->value; 12553a6ba7dcSVincent Cheng loaddr = rec->loaddr; 12563a6ba7dcSVincent Cheng 12573a6ba7dcSVincent Cheng rec++; 12583a6ba7dcSVincent Cheng 12593a6ba7dcSVincent Cheng err = check_and_set_masks(idtcm, regaddr, val); 12603a6ba7dcSVincent Cheng } 12613a6ba7dcSVincent Cheng 12627ea5fda2SMin Li if (err != -EINVAL) { 12637ea5fda2SMin Li err = 0; 12647ea5fda2SMin Li 12653a6ba7dcSVincent Cheng /* Top (status registers) and bottom are read-only */ 12663a6ba7dcSVincent Cheng if ((regaddr < GPIO_USER_CONTROL) 12673a6ba7dcSVincent Cheng || (regaddr >= SCRATCH)) 12683a6ba7dcSVincent Cheng continue; 12693a6ba7dcSVincent Cheng 12703a6ba7dcSVincent Cheng /* Page size 128, last 4 bytes of page skipped */ 12713a6ba7dcSVincent Cheng if (((loaddr > 0x7b) && (loaddr <= 0x7f)) 12723e14462fSYang Yingliang || loaddr > 0xfb) 12733a6ba7dcSVincent Cheng continue; 12743a6ba7dcSVincent Cheng 12753a6ba7dcSVincent Cheng err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val)); 12763a6ba7dcSVincent Cheng } 12773a6ba7dcSVincent Cheng 12783a6ba7dcSVincent Cheng if (err) 12793a6ba7dcSVincent Cheng goto out; 12803a6ba7dcSVincent Cheng } 12813a6ba7dcSVincent Cheng 12827ea5fda2SMin Li display_pll_and_masks(idtcm); 12833a6ba7dcSVincent Cheng 12843a6ba7dcSVincent Cheng out: 12853a6ba7dcSVincent Cheng release_firmware(fw); 12863a6ba7dcSVincent Cheng return err; 12873a6ba7dcSVincent Cheng } 12883a6ba7dcSVincent Cheng 12897ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel, 12907ea5fda2SMin Li bool enable, unsigned int outn) 12913a6ba7dcSVincent Cheng { 12923a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 12937260d1c8SMin Li int base; 12943a6ba7dcSVincent Cheng int err; 12957ea5fda2SMin Li u8 val; 12963a6ba7dcSVincent Cheng 12977260d1c8SMin Li base = get_output_base_addr(outn); 12987260d1c8SMin Li 12997260d1c8SMin Li if (!(base > 0)) { 13007260d1c8SMin Li dev_err(&idtcm->client->dev, 13017260d1c8SMin Li "%s - Unsupported out%d", __func__, outn); 13027260d1c8SMin Li return base; 13037260d1c8SMin Li } 13047260d1c8SMin Li 13057260d1c8SMin Li err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 13063a6ba7dcSVincent Cheng if (err) 13073a6ba7dcSVincent Cheng return err; 13083a6ba7dcSVincent Cheng 13093a6ba7dcSVincent Cheng if (enable) 13103a6ba7dcSVincent Cheng val |= SQUELCH_DISABLE; 13113a6ba7dcSVincent Cheng else 13123a6ba7dcSVincent Cheng val &= ~SQUELCH_DISABLE; 13133a6ba7dcSVincent Cheng 13147260d1c8SMin Li return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 13157ea5fda2SMin Li } 13167ea5fda2SMin Li 13177ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel, 13187ea5fda2SMin Li bool enable) 13197ea5fda2SMin Li { 13207ea5fda2SMin Li u16 mask; 13217ea5fda2SMin Li int err; 13227ea5fda2SMin Li u8 outn; 13237ea5fda2SMin Li 13247ea5fda2SMin Li mask = channel->output_mask; 13257ea5fda2SMin Li outn = 0; 13267ea5fda2SMin Li 13277ea5fda2SMin Li while (mask) { 13287ea5fda2SMin Li if (mask & 0x1) { 13297ea5fda2SMin Li err = idtcm_output_enable(channel, enable, outn); 13303a6ba7dcSVincent Cheng if (err) 13313a6ba7dcSVincent Cheng return err; 13327ea5fda2SMin Li } 13337ea5fda2SMin Li 13347ea5fda2SMin Li mask >>= 0x1; 13357ea5fda2SMin Li outn++; 13367ea5fda2SMin Li } 13373a6ba7dcSVincent Cheng 13383a6ba7dcSVincent Cheng return 0; 13393a6ba7dcSVincent Cheng } 13403a6ba7dcSVincent Cheng 13417ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel, 13427ea5fda2SMin Li bool enable, 13437ea5fda2SMin Li struct ptp_perout_request *perout) 13447ea5fda2SMin Li { 1345e8b4d8b5SVincent Cheng struct idtcm *idtcm = channel->idtcm; 13467ea5fda2SMin Li unsigned int flags = perout->flags; 1347e8b4d8b5SVincent Cheng struct timespec64 ts = {0, 0}; 1348e8b4d8b5SVincent Cheng int err; 13497ea5fda2SMin Li 13507ea5fda2SMin Li if (flags == PEROUT_ENABLE_OUTPUT_MASK) 1351e8b4d8b5SVincent Cheng err = idtcm_output_mask_enable(channel, enable); 1352e8b4d8b5SVincent Cheng else 1353e8b4d8b5SVincent Cheng err = idtcm_output_enable(channel, enable, perout->index); 13547ea5fda2SMin Li 1355e8b4d8b5SVincent Cheng if (err) { 1356e8b4d8b5SVincent Cheng dev_err(&idtcm->client->dev, "Unable to set output enable"); 1357e8b4d8b5SVincent Cheng return err; 1358e8b4d8b5SVincent Cheng } 1359e8b4d8b5SVincent Cheng 1360e8b4d8b5SVincent Cheng /* Align output to internal 1 PPS */ 1361e8b4d8b5SVincent Cheng return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS); 13627ea5fda2SMin Li } 13637ea5fda2SMin Li 13647260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel, 13657260d1c8SMin Li enum pll_mode *pll_mode) 13667260d1c8SMin Li { 13677260d1c8SMin Li struct idtcm *idtcm = channel->idtcm; 13687260d1c8SMin Li int err; 13697260d1c8SMin Li u8 dpll_mode; 13707260d1c8SMin Li 13717260d1c8SMin Li err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13727260d1c8SMin Li &dpll_mode, sizeof(dpll_mode)); 13737260d1c8SMin Li if (err) 13747260d1c8SMin Li return err; 13757260d1c8SMin Li 13767260d1c8SMin Li *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; 13777260d1c8SMin Li 13787260d1c8SMin Li return 0; 13797260d1c8SMin Li } 13807260d1c8SMin Li 13813a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel, 13823a6ba7dcSVincent Cheng enum pll_mode pll_mode) 13833a6ba7dcSVincent Cheng { 13843a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 13853a6ba7dcSVincent Cheng int err; 13863a6ba7dcSVincent Cheng u8 dpll_mode; 13873a6ba7dcSVincent Cheng 13883a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13893a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 13903a6ba7dcSVincent Cheng if (err) 13913a6ba7dcSVincent Cheng return err; 13923a6ba7dcSVincent Cheng 13933a6ba7dcSVincent Cheng dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); 13943a6ba7dcSVincent Cheng 13953a6ba7dcSVincent Cheng dpll_mode |= (pll_mode << PLL_MODE_SHIFT); 13963a6ba7dcSVincent Cheng 13973a6ba7dcSVincent Cheng channel->pll_mode = pll_mode; 13983a6ba7dcSVincent Cheng 13993a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE, 14003a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 14013a6ba7dcSVincent Cheng if (err) 14023a6ba7dcSVincent Cheng return err; 14033a6ba7dcSVincent Cheng 14043a6ba7dcSVincent Cheng return 0; 14053a6ba7dcSVincent Cheng } 14063a6ba7dcSVincent Cheng 14073a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */ 14083a6ba7dcSVincent Cheng 1409425d2b1cSVincent Cheng /** 1410425d2b1cSVincent Cheng * @brief Maximum absolute value for write phase offset in picoseconds 1411425d2b1cSVincent Cheng * 1412425d2b1cSVincent Cheng * Destination signed register is 32-bit register in resolution of 50ps 1413425d2b1cSVincent Cheng * 1414425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1415425d2b1cSVincent Cheng */ 1416425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) 1417425d2b1cSVincent Cheng { 1418425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1419425d2b1cSVincent Cheng int err; 1420425d2b1cSVincent Cheng u8 i; 1421425d2b1cSVincent Cheng u8 buf[4] = {0}; 1422425d2b1cSVincent Cheng s32 phase_50ps; 1423425d2b1cSVincent Cheng s64 offset_ps; 1424425d2b1cSVincent Cheng 1425425d2b1cSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_PHASE) { 1426425d2b1cSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); 1427425d2b1cSVincent Cheng if (err) 1428425d2b1cSVincent Cheng return err; 1429425d2b1cSVincent Cheng } 1430425d2b1cSVincent Cheng 1431425d2b1cSVincent Cheng offset_ps = (s64)delta_ns * 1000; 1432425d2b1cSVincent Cheng 1433425d2b1cSVincent Cheng /* 1434425d2b1cSVincent Cheng * Check for 32-bit signed max * 50: 1435425d2b1cSVincent Cheng * 1436425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1437425d2b1cSVincent Cheng */ 1438425d2b1cSVincent Cheng if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS) 1439425d2b1cSVincent Cheng offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS; 1440425d2b1cSVincent Cheng else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS) 1441425d2b1cSVincent Cheng offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS; 1442425d2b1cSVincent Cheng 14437260d1c8SMin Li phase_50ps = div_s64(offset_ps, 50); 1444425d2b1cSVincent Cheng 1445425d2b1cSVincent Cheng for (i = 0; i < 4; i++) { 1446425d2b1cSVincent Cheng buf[i] = phase_50ps & 0xff; 1447425d2b1cSVincent Cheng phase_50ps >>= 8; 1448425d2b1cSVincent Cheng } 1449425d2b1cSVincent Cheng 1450425d2b1cSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE, 1451425d2b1cSVincent Cheng buf, sizeof(buf)); 1452425d2b1cSVincent Cheng 1453425d2b1cSVincent Cheng return err; 1454425d2b1cSVincent Cheng } 1455425d2b1cSVincent Cheng 14567ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm) 14573a6ba7dcSVincent Cheng { 14583a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 14593a6ba7dcSVincent Cheng u8 i; 14603a6ba7dcSVincent Cheng int err; 14613a6ba7dcSVincent Cheng u8 buf[6] = {0}; 14623a6ba7dcSVincent Cheng s64 fcw; 14633a6ba7dcSVincent Cheng 14643a6ba7dcSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) { 14653a6ba7dcSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); 14663a6ba7dcSVincent Cheng if (err) 14673a6ba7dcSVincent Cheng return err; 14683a6ba7dcSVincent Cheng } 14693a6ba7dcSVincent Cheng 14703a6ba7dcSVincent Cheng /* 14713a6ba7dcSVincent Cheng * Frequency Control Word unit is: 1.11 * 10^-10 ppm 14723a6ba7dcSVincent Cheng * 14733a6ba7dcSVincent Cheng * adjfreq: 14743a6ba7dcSVincent Cheng * ppb * 10^9 14753a6ba7dcSVincent Cheng * FCW = ---------- 14763a6ba7dcSVincent Cheng * 111 14773a6ba7dcSVincent Cheng * 14783a6ba7dcSVincent Cheng * adjfine: 14793a6ba7dcSVincent Cheng * ppm_16 * 5^12 14803a6ba7dcSVincent Cheng * FCW = ------------- 14813a6ba7dcSVincent Cheng * 111 * 2^4 14823a6ba7dcSVincent Cheng */ 14833a6ba7dcSVincent Cheng 14843a6ba7dcSVincent Cheng /* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */ 14857ea5fda2SMin Li fcw = scaled_ppm * 244140625ULL; 14863a6ba7dcSVincent Cheng 14877260d1c8SMin Li fcw = div_s64(fcw, 1776); 14883a6ba7dcSVincent Cheng 14893a6ba7dcSVincent Cheng for (i = 0; i < 6; i++) { 14903a6ba7dcSVincent Cheng buf[i] = fcw & 0xff; 14913a6ba7dcSVincent Cheng fcw >>= 8; 14923a6ba7dcSVincent Cheng } 14933a6ba7dcSVincent Cheng 14943a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ, 14953a6ba7dcSVincent Cheng buf, sizeof(buf)); 14963a6ba7dcSVincent Cheng 14973a6ba7dcSVincent Cheng return err; 14983a6ba7dcSVincent Cheng } 14993a6ba7dcSVincent Cheng 15003a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 15013a6ba7dcSVincent Cheng { 1502fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15033a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15043a6ba7dcSVincent Cheng int err; 15053a6ba7dcSVincent Cheng 15063a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 15073a6ba7dcSVincent Cheng 15083a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, ts); 15097ea5fda2SMin Li if (err) 15101c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Failed at line %d in %s!", 15111c49d3e9SVincent Cheng __LINE__, __func__); 15127ea5fda2SMin Li 15133a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 15143a6ba7dcSVincent Cheng 15153a6ba7dcSVincent Cheng return err; 15163a6ba7dcSVincent Cheng } 15173a6ba7dcSVincent Cheng 1518da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp, 15193a6ba7dcSVincent Cheng const struct timespec64 *ts) 15203a6ba7dcSVincent Cheng { 1521fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15223a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15233a6ba7dcSVincent Cheng int err; 15243a6ba7dcSVincent Cheng 15253a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 15263a6ba7dcSVincent Cheng 1527da948233SMin Li err = _idtcm_settime_deprecated(channel, ts); 15287ea5fda2SMin Li if (err) 15297ea5fda2SMin Li dev_err(&idtcm->client->dev, 15301c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15317ea5fda2SMin Li 15327ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 15337ea5fda2SMin Li 15347ea5fda2SMin Li return err; 15357ea5fda2SMin Li } 15367ea5fda2SMin Li 1537da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp, 15387ea5fda2SMin Li const struct timespec64 *ts) 15397ea5fda2SMin Li { 1540fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15417ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 15427ea5fda2SMin Li int err; 15437ea5fda2SMin Li 15447ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 15457ea5fda2SMin Li 1546da948233SMin Li err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 1547da948233SMin Li if (err) 1548da948233SMin Li dev_err(&idtcm->client->dev, 15491c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 1550da948233SMin Li 1551da948233SMin Li mutex_unlock(&idtcm->reg_lock); 1552da948233SMin Li 1553da948233SMin Li return err; 1554da948233SMin Li } 1555da948233SMin Li 1556da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta) 1557da948233SMin Li { 1558fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 1559da948233SMin Li struct idtcm *idtcm = channel->idtcm; 1560da948233SMin Li int err; 1561da948233SMin Li 1562da948233SMin Li mutex_lock(&idtcm->reg_lock); 1563da948233SMin Li 1564da948233SMin Li err = _idtcm_adjtime_deprecated(channel, delta); 15657ea5fda2SMin Li if (err) 15667ea5fda2SMin Li dev_err(&idtcm->client->dev, 15671c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15687ea5fda2SMin Li 15693a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 15703a6ba7dcSVincent Cheng 15713a6ba7dcSVincent Cheng return err; 15723a6ba7dcSVincent Cheng } 15733a6ba7dcSVincent Cheng 15743a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta) 15753a6ba7dcSVincent Cheng { 1576fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15773a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15787ea5fda2SMin Li struct timespec64 ts; 15797ea5fda2SMin Li enum scsr_tod_write_type_sel type; 15807ea5fda2SMin Li int err; 15817ea5fda2SMin Li 1582da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { 15837ea5fda2SMin Li err = idtcm_do_phase_pull_in(channel, delta, 0); 15847ea5fda2SMin Li if (err) 15857ea5fda2SMin Li dev_err(&idtcm->client->dev, 15861c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15877ea5fda2SMin Li return err; 15887ea5fda2SMin Li } 15897ea5fda2SMin Li 15907ea5fda2SMin Li if (delta >= 0) { 15917ea5fda2SMin Li ts = ns_to_timespec64(delta); 15927ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; 15937ea5fda2SMin Li } else { 15947ea5fda2SMin Li ts = ns_to_timespec64(-delta); 15957ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; 15967ea5fda2SMin Li } 15977ea5fda2SMin Li 15987ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 15997ea5fda2SMin Li 1600da948233SMin Li err = _idtcm_settime(channel, &ts, type); 16017ea5fda2SMin Li if (err) 16027ea5fda2SMin Li dev_err(&idtcm->client->dev, 16031c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16047ea5fda2SMin Li 16053a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 16063a6ba7dcSVincent Cheng 16073a6ba7dcSVincent Cheng return err; 16083a6ba7dcSVincent Cheng } 16093a6ba7dcSVincent Cheng 1610425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta) 1611425d2b1cSVincent Cheng { 1612fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 1613425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1614425d2b1cSVincent Cheng int err; 1615425d2b1cSVincent Cheng 1616425d2b1cSVincent Cheng mutex_lock(&idtcm->reg_lock); 1617425d2b1cSVincent Cheng 1618425d2b1cSVincent Cheng err = _idtcm_adjphase(channel, delta); 16197ea5fda2SMin Li if (err) 16207ea5fda2SMin Li dev_err(&idtcm->client->dev, 16211c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16227ea5fda2SMin Li 16237ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 16247ea5fda2SMin Li 16257ea5fda2SMin Li return err; 16267ea5fda2SMin Li } 16277ea5fda2SMin Li 16287ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 16297ea5fda2SMin Li { 1630fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 16317ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 16327ea5fda2SMin Li int err; 16337ea5fda2SMin Li 16347ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 16357ea5fda2SMin Li 16367ea5fda2SMin Li err = _idtcm_adjfine(channel, scaled_ppm); 16377ea5fda2SMin Li if (err) 16387ea5fda2SMin Li dev_err(&idtcm->client->dev, 16391c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16407ea5fda2SMin Li 1641425d2b1cSVincent Cheng mutex_unlock(&idtcm->reg_lock); 1642425d2b1cSVincent Cheng 1643425d2b1cSVincent Cheng return err; 1644425d2b1cSVincent Cheng } 1645425d2b1cSVincent Cheng 16463a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp, 16473a6ba7dcSVincent Cheng struct ptp_clock_request *rq, int on) 16483a6ba7dcSVincent Cheng { 16497ea5fda2SMin Li int err; 1650fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 16513a6ba7dcSVincent Cheng 16523a6ba7dcSVincent Cheng switch (rq->type) { 16533a6ba7dcSVincent Cheng case PTP_CLK_REQ_PEROUT: 16547ea5fda2SMin Li if (!on) { 16557ea5fda2SMin Li err = idtcm_perout_enable(channel, false, &rq->perout); 16567ea5fda2SMin Li if (err) 16577ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16581c49d3e9SVincent Cheng "Failed at line %d in %s!", 16591c49d3e9SVincent Cheng __LINE__, __func__); 16607ea5fda2SMin Li return err; 16617ea5fda2SMin Li } 16623a6ba7dcSVincent Cheng 16633a6ba7dcSVincent Cheng /* Only accept a 1-PPS aligned to the second. */ 16643a6ba7dcSVincent Cheng if (rq->perout.start.nsec || rq->perout.period.sec != 1 || 16653a6ba7dcSVincent Cheng rq->perout.period.nsec) 16663a6ba7dcSVincent Cheng return -ERANGE; 16673a6ba7dcSVincent Cheng 16687ea5fda2SMin Li err = idtcm_perout_enable(channel, true, &rq->perout); 16697ea5fda2SMin Li if (err) 16707ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16711c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16727ea5fda2SMin Li return err; 16733a6ba7dcSVincent Cheng default: 16743a6ba7dcSVincent Cheng break; 16753a6ba7dcSVincent Cheng } 16763a6ba7dcSVincent Cheng 16773a6ba7dcSVincent Cheng return -EOPNOTSUPP; 16783a6ba7dcSVincent Cheng } 16793a6ba7dcSVincent Cheng 16807ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm, 16817ea5fda2SMin Li u8 pll, 16827ea5fda2SMin Li u8 sync_src, 16837ea5fda2SMin Li u8 qn, 16847ea5fda2SMin Li u8 qn_plus_1) 16857ea5fda2SMin Li { 16867ea5fda2SMin Li int err; 16877ea5fda2SMin Li u8 val; 16887ea5fda2SMin Li u16 dpll; 16897ea5fda2SMin Li u16 out0 = 0, out1 = 0; 16907ea5fda2SMin Li 16917ea5fda2SMin Li if ((qn == 0) && (qn_plus_1 == 0)) 16927ea5fda2SMin Li return 0; 16937ea5fda2SMin Li 16947ea5fda2SMin Li switch (pll) { 16957ea5fda2SMin Li case 0: 16967ea5fda2SMin Li dpll = DPLL_0; 16977ea5fda2SMin Li if (qn) 16987ea5fda2SMin Li out0 = OUTPUT_0; 16997ea5fda2SMin Li if (qn_plus_1) 17007ea5fda2SMin Li out1 = OUTPUT_1; 17017ea5fda2SMin Li break; 17027ea5fda2SMin Li case 1: 17037ea5fda2SMin Li dpll = DPLL_1; 17047ea5fda2SMin Li if (qn) 17057ea5fda2SMin Li out0 = OUTPUT_2; 17067ea5fda2SMin Li if (qn_plus_1) 17077ea5fda2SMin Li out1 = OUTPUT_3; 17087ea5fda2SMin Li break; 17097ea5fda2SMin Li case 2: 17107ea5fda2SMin Li dpll = DPLL_2; 17117ea5fda2SMin Li if (qn) 17127ea5fda2SMin Li out0 = OUTPUT_4; 17137ea5fda2SMin Li if (qn_plus_1) 17147ea5fda2SMin Li out1 = OUTPUT_5; 17157ea5fda2SMin Li break; 17167ea5fda2SMin Li case 3: 17177ea5fda2SMin Li dpll = DPLL_3; 17187ea5fda2SMin Li if (qn) 17197ea5fda2SMin Li out0 = OUTPUT_6; 17207ea5fda2SMin Li if (qn_plus_1) 17217ea5fda2SMin Li out1 = OUTPUT_7; 17227ea5fda2SMin Li break; 17237ea5fda2SMin Li case 4: 17247ea5fda2SMin Li dpll = DPLL_4; 17257ea5fda2SMin Li if (qn) 17267ea5fda2SMin Li out0 = OUTPUT_8; 17277ea5fda2SMin Li break; 17287ea5fda2SMin Li case 5: 17297ea5fda2SMin Li dpll = DPLL_5; 17307ea5fda2SMin Li if (qn) 17317ea5fda2SMin Li out0 = OUTPUT_9; 17327ea5fda2SMin Li if (qn_plus_1) 17337ea5fda2SMin Li out1 = OUTPUT_8; 17347ea5fda2SMin Li break; 17357ea5fda2SMin Li case 6: 17367ea5fda2SMin Li dpll = DPLL_6; 17377ea5fda2SMin Li if (qn) 17387ea5fda2SMin Li out0 = OUTPUT_10; 17397ea5fda2SMin Li if (qn_plus_1) 17407ea5fda2SMin Li out1 = OUTPUT_11; 17417ea5fda2SMin Li break; 17427ea5fda2SMin Li case 7: 17437ea5fda2SMin Li dpll = DPLL_7; 17447ea5fda2SMin Li if (qn) 17457ea5fda2SMin Li out0 = OUTPUT_11; 17467ea5fda2SMin Li break; 17477ea5fda2SMin Li default: 17487ea5fda2SMin Li return -EINVAL; 17497ea5fda2SMin Li } 17507ea5fda2SMin Li 17517ea5fda2SMin Li /* 17527ea5fda2SMin Li * Enable OUTPUT OUT_SYNC. 17537ea5fda2SMin Li */ 17547ea5fda2SMin Li if (out0) { 17557ea5fda2SMin Li err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17567ea5fda2SMin Li if (err) 17577ea5fda2SMin Li return err; 17587ea5fda2SMin Li 17597ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17607ea5fda2SMin Li 17617ea5fda2SMin Li err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17627ea5fda2SMin Li if (err) 17637ea5fda2SMin Li return err; 17647ea5fda2SMin Li } 17657ea5fda2SMin Li 17667ea5fda2SMin Li if (out1) { 17677ea5fda2SMin Li err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17687ea5fda2SMin Li if (err) 17697ea5fda2SMin Li return err; 17707ea5fda2SMin Li 17717ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17727ea5fda2SMin Li 17737ea5fda2SMin Li err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17747ea5fda2SMin Li if (err) 17757ea5fda2SMin Li return err; 17767ea5fda2SMin Li } 17777ea5fda2SMin Li 17787ea5fda2SMin Li /* enable dpll sync tod pps, must be set before dpll_mode */ 17797ea5fda2SMin Li err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 17807ea5fda2SMin Li if (err) 17817ea5fda2SMin Li return err; 17827ea5fda2SMin Li 17837ea5fda2SMin Li val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT); 17847ea5fda2SMin Li val |= (sync_src << TOD_SYNC_SOURCE_SHIFT); 17857ea5fda2SMin Li val |= TOD_SYNC_EN; 17867ea5fda2SMin Li 17877ea5fda2SMin Li return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 17887ea5fda2SMin Li } 17897ea5fda2SMin Li 17907ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel) 17917ea5fda2SMin Li { 17927ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 17937ea5fda2SMin Li u8 pll; 17947ea5fda2SMin Li u8 sync_src; 17957ea5fda2SMin Li u8 qn; 17967ea5fda2SMin Li u8 qn_plus_1; 17977ea5fda2SMin Li u8 cfg; 17987ea5fda2SMin Li int err = 0; 17997ea5fda2SMin Li u16 output_mask = channel->output_mask; 18007ea5fda2SMin Li u8 out8_mux = 0; 18017ea5fda2SMin Li u8 out11_mux = 0; 18027ea5fda2SMin Li u8 temp; 18037ea5fda2SMin Li 18047ea5fda2SMin Li /* 18057ea5fda2SMin Li * set tod_out_sync_enable to 0. 18067ea5fda2SMin Li */ 18077ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18087ea5fda2SMin Li if (err) 18097ea5fda2SMin Li return err; 18107ea5fda2SMin Li 18117ea5fda2SMin Li cfg &= ~TOD_OUT_SYNC_ENABLE; 18127ea5fda2SMin Li 18137ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18147ea5fda2SMin Li if (err) 18157ea5fda2SMin Li return err; 18167ea5fda2SMin Li 18177ea5fda2SMin Li switch (channel->tod_n) { 18187ea5fda2SMin Li case TOD_0: 18197ea5fda2SMin Li sync_src = 0; 18207ea5fda2SMin Li break; 18217ea5fda2SMin Li case TOD_1: 18227ea5fda2SMin Li sync_src = 1; 18237ea5fda2SMin Li break; 18247ea5fda2SMin Li case TOD_2: 18257ea5fda2SMin Li sync_src = 2; 18267ea5fda2SMin Li break; 18277ea5fda2SMin Li case TOD_3: 18287ea5fda2SMin Li sync_src = 3; 18297ea5fda2SMin Li break; 18307ea5fda2SMin Li default: 18317ea5fda2SMin Li return -EINVAL; 18327ea5fda2SMin Li } 18337ea5fda2SMin Li 1834fcfd3757SVincent Cheng err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp)); 18357ea5fda2SMin Li if (err) 18367ea5fda2SMin Li return err; 18377ea5fda2SMin Li 18387ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18397ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18407ea5fda2SMin Li out8_mux = 1; 18417ea5fda2SMin Li 1842fcfd3757SVincent Cheng err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp)); 18437ea5fda2SMin Li if (err) 18447ea5fda2SMin Li return err; 18457ea5fda2SMin Li 18467ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18477ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18487ea5fda2SMin Li out11_mux = 1; 18497ea5fda2SMin Li 18507ea5fda2SMin Li for (pll = 0; pll < 8; pll++) { 18517ea5fda2SMin Li qn = 0; 18527ea5fda2SMin Li qn_plus_1 = 0; 18537ea5fda2SMin Li 18547ea5fda2SMin Li if (pll < 4) { 18557ea5fda2SMin Li /* First 4 pll has 2 outputs */ 18567ea5fda2SMin Li qn = output_mask & 0x1; 18577ea5fda2SMin Li output_mask = output_mask >> 1; 18587ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18597ea5fda2SMin Li output_mask = output_mask >> 1; 18607ea5fda2SMin Li } else if (pll == 4) { 18617ea5fda2SMin Li if (out8_mux == 0) { 18627ea5fda2SMin Li qn = output_mask & 0x1; 18637ea5fda2SMin Li output_mask = output_mask >> 1; 18647ea5fda2SMin Li } 18657ea5fda2SMin Li } else if (pll == 5) { 18667ea5fda2SMin Li if (out8_mux) { 18677ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18687ea5fda2SMin Li output_mask = output_mask >> 1; 18697ea5fda2SMin Li } 18707ea5fda2SMin Li qn = output_mask & 0x1; 18717ea5fda2SMin Li output_mask = output_mask >> 1; 18727ea5fda2SMin Li } else if (pll == 6) { 18737ea5fda2SMin Li qn = output_mask & 0x1; 18747ea5fda2SMin Li output_mask = output_mask >> 1; 18757ea5fda2SMin Li if (out11_mux) { 18767ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18777ea5fda2SMin Li output_mask = output_mask >> 1; 18787ea5fda2SMin Li } 18797ea5fda2SMin Li } else if (pll == 7) { 18807ea5fda2SMin Li if (out11_mux == 0) { 18817ea5fda2SMin Li qn = output_mask & 0x1; 18827ea5fda2SMin Li output_mask = output_mask >> 1; 18837ea5fda2SMin Li } 18847ea5fda2SMin Li } 18857ea5fda2SMin Li 18867ea5fda2SMin Li if ((qn != 0) || (qn_plus_1 != 0)) 18877ea5fda2SMin Li err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn, 18887ea5fda2SMin Li qn_plus_1); 18897ea5fda2SMin Li if (err) 18907ea5fda2SMin Li return err; 18917ea5fda2SMin Li } 18927ea5fda2SMin Li 18937ea5fda2SMin Li return err; 18947ea5fda2SMin Li } 18957ea5fda2SMin Li 18963a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel) 18973a6ba7dcSVincent Cheng { 18983a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 18993a6ba7dcSVincent Cheng struct timespec64 ts = {0, 0}; 19003a6ba7dcSVincent Cheng u8 cfg; 19013a6ba7dcSVincent Cheng int err; 19023a6ba7dcSVincent Cheng 19033a6ba7dcSVincent Cheng /* 19043a6ba7dcSVincent Cheng * Start the TOD clock ticking. 19053a6ba7dcSVincent Cheng */ 19063a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19073a6ba7dcSVincent Cheng if (err) 19083a6ba7dcSVincent Cheng return err; 19093a6ba7dcSVincent Cheng 19103a6ba7dcSVincent Cheng cfg |= TOD_ENABLE; 19113a6ba7dcSVincent Cheng 19123a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19133a6ba7dcSVincent Cheng if (err) 19143a6ba7dcSVincent Cheng return err; 19153a6ba7dcSVincent Cheng 1916da948233SMin Li if (idtcm->deprecated) 1917da948233SMin Li return _idtcm_settime_deprecated(channel, &ts); 1918da948233SMin Li else 1919da948233SMin Li return _idtcm_settime(channel, &ts, 1920da948233SMin Li SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 19213a6ba7dcSVincent Cheng } 19223a6ba7dcSVincent Cheng 1923da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm) 19243a6ba7dcSVincent Cheng { 19253a6ba7dcSVincent Cheng u8 major; 19263a6ba7dcSVincent Cheng u8 minor; 19273a6ba7dcSVincent Cheng u8 hotfix; 19283a6ba7dcSVincent Cheng u16 product_id; 19293a6ba7dcSVincent Cheng u8 hw_rev_id; 19301ece2fbeSVincent Cheng u8 config_select; 19313a6ba7dcSVincent Cheng 19323a6ba7dcSVincent Cheng idtcm_read_major_release(idtcm, &major); 19333a6ba7dcSVincent Cheng idtcm_read_minor_release(idtcm, &minor); 19343a6ba7dcSVincent Cheng idtcm_read_hotfix_release(idtcm, &hotfix); 19353a6ba7dcSVincent Cheng 19363a6ba7dcSVincent Cheng idtcm_read_product_id(idtcm, &product_id); 19373a6ba7dcSVincent Cheng idtcm_read_hw_rev_id(idtcm, &hw_rev_id); 19383a6ba7dcSVincent Cheng 19391ece2fbeSVincent Cheng idtcm_read_otp_scsr_config_select(idtcm, &config_select); 19401ece2fbeSVincent Cheng 19417ea5fda2SMin Li snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u", 19427ea5fda2SMin Li major, minor, hotfix); 19437ea5fda2SMin Li 1944da948233SMin Li if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) 1945da948233SMin Li idtcm->deprecated = 0; 1946da948233SMin Li else 1947da948233SMin Li idtcm->deprecated = 1; 1948da948233SMin Li 19491c49d3e9SVincent Cheng dev_info(&idtcm->client->dev, 19501c49d3e9SVincent Cheng "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d", 19511c49d3e9SVincent Cheng major, minor, hotfix, 19521ece2fbeSVincent Cheng product_id, hw_rev_id, config_select); 19533a6ba7dcSVincent Cheng } 19543a6ba7dcSVincent Cheng 19556485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = { 19563a6ba7dcSVincent Cheng .owner = THIS_MODULE, 19573a6ba7dcSVincent Cheng .max_adj = 244000, 19587ea5fda2SMin Li .n_per_out = 12, 1959425d2b1cSVincent Cheng .adjphase = &idtcm_adjphase, 19607ea5fda2SMin Li .adjfine = &idtcm_adjfine, 19613a6ba7dcSVincent Cheng .adjtime = &idtcm_adjtime, 19623a6ba7dcSVincent Cheng .gettime64 = &idtcm_gettime, 19633a6ba7dcSVincent Cheng .settime64 = &idtcm_settime, 19643a6ba7dcSVincent Cheng .enable = &idtcm_enable, 19653a6ba7dcSVincent Cheng }; 19663a6ba7dcSVincent Cheng 1967da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = { 1968da948233SMin Li .owner = THIS_MODULE, 1969da948233SMin Li .max_adj = 244000, 1970da948233SMin Li .n_per_out = 12, 1971da948233SMin Li .adjphase = &idtcm_adjphase, 1972da948233SMin Li .adjfine = &idtcm_adjfine, 1973da948233SMin Li .adjtime = &idtcm_adjtime_deprecated, 1974da948233SMin Li .gettime64 = &idtcm_gettime, 1975da948233SMin Li .settime64 = &idtcm_settime_deprecated, 1976da948233SMin Li .enable = &idtcm_enable, 1977da948233SMin Li }; 1978da948233SMin Li 19797ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel) 19803a6ba7dcSVincent Cheng { 19817ea5fda2SMin Li int err = 0; 19823a6ba7dcSVincent Cheng 19837ea5fda2SMin Li switch (channel->pll) { 19843a6ba7dcSVincent Cheng case 0: 19853a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_0; 19863a6ba7dcSVincent Cheng channel->dpll_n = DPLL_0; 19873a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_0; 19883a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_0; 19893a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_0; 19903a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0; 19913a6ba7dcSVincent Cheng break; 19923a6ba7dcSVincent Cheng case 1: 19933a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_1; 19943a6ba7dcSVincent Cheng channel->dpll_n = DPLL_1; 19953a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_1; 19963a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_1; 19973a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_1; 19983a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1; 19993a6ba7dcSVincent Cheng break; 20003a6ba7dcSVincent Cheng case 2: 20013a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_2; 20023a6ba7dcSVincent Cheng channel->dpll_n = DPLL_2; 20033a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_2; 20043a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_2; 20053a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_2; 20063a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2; 20073a6ba7dcSVincent Cheng break; 20083a6ba7dcSVincent Cheng case 3: 20093a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_3; 20103a6ba7dcSVincent Cheng channel->dpll_n = DPLL_3; 20113a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_3; 20123a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_3; 20133a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_3; 20143a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3; 20153a6ba7dcSVincent Cheng break; 20167ea5fda2SMin Li case 4: 20177ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_4; 20187ea5fda2SMin Li channel->dpll_n = DPLL_4; 20197ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_4; 20207ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_4; 20217ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_4; 20227ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4; 20237ea5fda2SMin Li break; 20247ea5fda2SMin Li case 5: 20257ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_5; 20267ea5fda2SMin Li channel->dpll_n = DPLL_5; 20277ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_5; 20287ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_5; 20297ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_5; 20307ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5; 20317ea5fda2SMin Li break; 20327ea5fda2SMin Li case 6: 20337ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_6; 20347ea5fda2SMin Li channel->dpll_n = DPLL_6; 20357ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_6; 20367ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_6; 20377ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_6; 20387ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6; 20397ea5fda2SMin Li break; 20407ea5fda2SMin Li case 7: 20417ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_7; 20427ea5fda2SMin Li channel->dpll_n = DPLL_7; 20437ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_7; 20447ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_7; 20457ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_7; 20467ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7; 20477ea5fda2SMin Li break; 20487ea5fda2SMin Li default: 20497ea5fda2SMin Li err = -EINVAL; 20507ea5fda2SMin Li } 20517ea5fda2SMin Li 20527ea5fda2SMin Li return err; 20537ea5fda2SMin Li } 20547ea5fda2SMin Li 20557ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) 20567ea5fda2SMin Li { 20577ea5fda2SMin Li struct idtcm_channel *channel; 20587ea5fda2SMin Li int err; 20597ea5fda2SMin Li 20607ea5fda2SMin Li if (!(index < MAX_TOD)) 20617ea5fda2SMin Li return -EINVAL; 20627ea5fda2SMin Li 20637ea5fda2SMin Li channel = &idtcm->channel[index]; 20647ea5fda2SMin Li 20657ea5fda2SMin Li /* Set pll addresses */ 20667ea5fda2SMin Li err = configure_channel_pll(channel); 20677ea5fda2SMin Li if (err) 20687ea5fda2SMin Li return err; 20697ea5fda2SMin Li 20707ea5fda2SMin Li /* Set tod addresses */ 20717ea5fda2SMin Li switch (index) { 20727ea5fda2SMin Li case 0: 20737ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_0; 20747ea5fda2SMin Li channel->tod_write = TOD_WRITE_0; 20757ea5fda2SMin Li channel->tod_n = TOD_0; 20767ea5fda2SMin Li break; 20777ea5fda2SMin Li case 1: 20787ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_1; 20797ea5fda2SMin Li channel->tod_write = TOD_WRITE_1; 20807ea5fda2SMin Li channel->tod_n = TOD_1; 20817ea5fda2SMin Li break; 20827ea5fda2SMin Li case 2: 20837ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_2; 20847ea5fda2SMin Li channel->tod_write = TOD_WRITE_2; 20857ea5fda2SMin Li channel->tod_n = TOD_2; 20867ea5fda2SMin Li break; 20877ea5fda2SMin Li case 3: 20887ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_3; 20897ea5fda2SMin Li channel->tod_write = TOD_WRITE_3; 20907ea5fda2SMin Li channel->tod_n = TOD_3; 20917ea5fda2SMin Li break; 20923a6ba7dcSVincent Cheng default: 20933a6ba7dcSVincent Cheng return -EINVAL; 20943a6ba7dcSVincent Cheng } 20953a6ba7dcSVincent Cheng 20963a6ba7dcSVincent Cheng channel->idtcm = idtcm; 20973a6ba7dcSVincent Cheng 2098da948233SMin Li if (idtcm->deprecated) 2099da948233SMin Li channel->caps = idtcm_caps_deprecated; 21007ea5fda2SMin Li else 21013a6ba7dcSVincent Cheng channel->caps = idtcm_caps; 21027ea5fda2SMin Li 21033a6ba7dcSVincent Cheng snprintf(channel->caps.name, sizeof(channel->caps.name), 21047ea5fda2SMin Li "IDT CM TOD%u", index); 21057ea5fda2SMin Li 2106da948233SMin Li if (!idtcm->deprecated) { 21077ea5fda2SMin Li err = idtcm_enable_tod_sync(channel); 21087ea5fda2SMin Li if (err) { 21097ea5fda2SMin Li dev_err(&idtcm->client->dev, 21101c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 21117ea5fda2SMin Li return err; 21127ea5fda2SMin Li } 21137ea5fda2SMin Li } 21143a6ba7dcSVincent Cheng 21157260d1c8SMin Li /* Sync pll mode with hardware */ 21167260d1c8SMin Li err = idtcm_get_pll_mode(channel, &channel->pll_mode); 21177ea5fda2SMin Li if (err) { 21187ea5fda2SMin Li dev_err(&idtcm->client->dev, 21191c49d3e9SVincent Cheng "Error: %s - Unable to read pll mode", __func__); 21203a6ba7dcSVincent Cheng return err; 21217ea5fda2SMin Li } 21223a6ba7dcSVincent Cheng 21233a6ba7dcSVincent Cheng err = idtcm_enable_tod(channel); 21247ea5fda2SMin Li if (err) { 21257ea5fda2SMin Li dev_err(&idtcm->client->dev, 21261c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 21273a6ba7dcSVincent Cheng return err; 21287ea5fda2SMin Li } 21293a6ba7dcSVincent Cheng 21303a6ba7dcSVincent Cheng channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); 21313a6ba7dcSVincent Cheng 21323a6ba7dcSVincent Cheng if (IS_ERR(channel->ptp_clock)) { 21333a6ba7dcSVincent Cheng err = PTR_ERR(channel->ptp_clock); 21343a6ba7dcSVincent Cheng channel->ptp_clock = NULL; 21353a6ba7dcSVincent Cheng return err; 21363a6ba7dcSVincent Cheng } 21373a6ba7dcSVincent Cheng 21383a6ba7dcSVincent Cheng if (!channel->ptp_clock) 21393a6ba7dcSVincent Cheng return -ENOTSUPP; 21403a6ba7dcSVincent Cheng 21411c49d3e9SVincent Cheng dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d", 21423a6ba7dcSVincent Cheng index, channel->ptp_clock->index); 21433a6ba7dcSVincent Cheng 21443a6ba7dcSVincent Cheng return 0; 21453a6ba7dcSVincent Cheng } 21463a6ba7dcSVincent Cheng 21473a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm) 21483a6ba7dcSVincent Cheng { 21493a6ba7dcSVincent Cheng u8 i; 21503a6ba7dcSVincent Cheng struct idtcm_channel *channel; 21513a6ba7dcSVincent Cheng 21527ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 21533a6ba7dcSVincent Cheng channel = &idtcm->channel[i]; 21543a6ba7dcSVincent Cheng 21553a6ba7dcSVincent Cheng if (channel->ptp_clock) 21563a6ba7dcSVincent Cheng ptp_clock_unregister(channel->ptp_clock); 21573a6ba7dcSVincent Cheng } 21583a6ba7dcSVincent Cheng } 21593a6ba7dcSVincent Cheng 21603a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm) 21613a6ba7dcSVincent Cheng { 21627ea5fda2SMin Li idtcm->tod_mask = DEFAULT_TOD_MASK; 21637ea5fda2SMin Li 21647ea5fda2SMin Li idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL; 21657ea5fda2SMin Li idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL; 21667ea5fda2SMin Li idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL; 21677ea5fda2SMin Li idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL; 21683a6ba7dcSVincent Cheng 21693a6ba7dcSVincent Cheng idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; 21703a6ba7dcSVincent Cheng idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; 21713a6ba7dcSVincent Cheng idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2; 21723a6ba7dcSVincent Cheng idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3; 21733a6ba7dcSVincent Cheng } 21743a6ba7dcSVincent Cheng 21753a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client, 21763a6ba7dcSVincent Cheng const struct i2c_device_id *id) 21773a6ba7dcSVincent Cheng { 21783a6ba7dcSVincent Cheng struct idtcm *idtcm; 21793a6ba7dcSVincent Cheng int err; 21803a6ba7dcSVincent Cheng u8 i; 21813a6ba7dcSVincent Cheng 21823a6ba7dcSVincent Cheng /* Unused for now */ 21833a6ba7dcSVincent Cheng (void)id; 21843a6ba7dcSVincent Cheng 21853a6ba7dcSVincent Cheng idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL); 21863a6ba7dcSVincent Cheng 21873a6ba7dcSVincent Cheng if (!idtcm) 21883a6ba7dcSVincent Cheng return -ENOMEM; 21893a6ba7dcSVincent Cheng 21903a6ba7dcSVincent Cheng idtcm->client = client; 21913a6ba7dcSVincent Cheng idtcm->page_offset = 0xff; 21923a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 21933a6ba7dcSVincent Cheng 21943a6ba7dcSVincent Cheng set_default_masks(idtcm); 21953a6ba7dcSVincent Cheng 21963a6ba7dcSVincent Cheng mutex_init(&idtcm->reg_lock); 21973a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 21983a6ba7dcSVincent Cheng 2199da948233SMin Li idtcm_set_version_info(idtcm); 22003a6ba7dcSVincent Cheng 22013a6ba7dcSVincent Cheng err = idtcm_load_firmware(idtcm, &client->dev); 22023a6ba7dcSVincent Cheng if (err) 2203fcfd3757SVincent Cheng dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err); 22043a6ba7dcSVincent Cheng 2205797d3186SVincent Cheng wait_for_chip_ready(idtcm); 2206251f4fe2SMin Li 22077ea5fda2SMin Li if (idtcm->tod_mask) { 22087ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 22097ea5fda2SMin Li if (idtcm->tod_mask & (1 << i)) { 22103a6ba7dcSVincent Cheng err = idtcm_enable_channel(idtcm, i); 22117ea5fda2SMin Li if (err) { 22127ea5fda2SMin Li dev_err(&idtcm->client->dev, 22131c49d3e9SVincent Cheng "idtcm_enable_channel %d failed!", i); 22143a6ba7dcSVincent Cheng break; 22153a6ba7dcSVincent Cheng } 22163a6ba7dcSVincent Cheng } 22177ea5fda2SMin Li } 22183a6ba7dcSVincent Cheng } else { 22193a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 22201c49d3e9SVincent Cheng "no PLLs flagged as PHCs, nothing to do"); 22213a6ba7dcSVincent Cheng err = -ENODEV; 22223a6ba7dcSVincent Cheng } 22233a6ba7dcSVincent Cheng 22243a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 22253a6ba7dcSVincent Cheng 22263a6ba7dcSVincent Cheng if (err) { 22273a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22283a6ba7dcSVincent Cheng return err; 22293a6ba7dcSVincent Cheng } 22303a6ba7dcSVincent Cheng 22313a6ba7dcSVincent Cheng i2c_set_clientdata(client, idtcm); 22323a6ba7dcSVincent Cheng 22333a6ba7dcSVincent Cheng return 0; 22343a6ba7dcSVincent Cheng } 22353a6ba7dcSVincent Cheng 22363a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client) 22373a6ba7dcSVincent Cheng { 22383a6ba7dcSVincent Cheng struct idtcm *idtcm = i2c_get_clientdata(client); 22393a6ba7dcSVincent Cheng 22403a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22413a6ba7dcSVincent Cheng 22423a6ba7dcSVincent Cheng mutex_destroy(&idtcm->reg_lock); 22433a6ba7dcSVincent Cheng 22443a6ba7dcSVincent Cheng return 0; 22453a6ba7dcSVincent Cheng } 22463a6ba7dcSVincent Cheng 22473a6ba7dcSVincent Cheng #ifdef CONFIG_OF 22483a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = { 22493a6ba7dcSVincent Cheng { .compatible = "idt,8a34000" }, 22503a6ba7dcSVincent Cheng { .compatible = "idt,8a34001" }, 22513a6ba7dcSVincent Cheng { .compatible = "idt,8a34002" }, 22523a6ba7dcSVincent Cheng { .compatible = "idt,8a34003" }, 22533a6ba7dcSVincent Cheng { .compatible = "idt,8a34004" }, 22543a6ba7dcSVincent Cheng { .compatible = "idt,8a34005" }, 22553a6ba7dcSVincent Cheng { .compatible = "idt,8a34006" }, 22563a6ba7dcSVincent Cheng { .compatible = "idt,8a34007" }, 22573a6ba7dcSVincent Cheng { .compatible = "idt,8a34008" }, 22583a6ba7dcSVincent Cheng { .compatible = "idt,8a34009" }, 22593a6ba7dcSVincent Cheng { .compatible = "idt,8a34010" }, 22603a6ba7dcSVincent Cheng { .compatible = "idt,8a34011" }, 22613a6ba7dcSVincent Cheng { .compatible = "idt,8a34012" }, 22623a6ba7dcSVincent Cheng { .compatible = "idt,8a34013" }, 22633a6ba7dcSVincent Cheng { .compatible = "idt,8a34014" }, 22643a6ba7dcSVincent Cheng { .compatible = "idt,8a34015" }, 22653a6ba7dcSVincent Cheng { .compatible = "idt,8a34016" }, 22663a6ba7dcSVincent Cheng { .compatible = "idt,8a34017" }, 22673a6ba7dcSVincent Cheng { .compatible = "idt,8a34018" }, 22683a6ba7dcSVincent Cheng { .compatible = "idt,8a34019" }, 22693a6ba7dcSVincent Cheng { .compatible = "idt,8a34040" }, 22703a6ba7dcSVincent Cheng { .compatible = "idt,8a34041" }, 22713a6ba7dcSVincent Cheng { .compatible = "idt,8a34042" }, 22723a6ba7dcSVincent Cheng { .compatible = "idt,8a34043" }, 22733a6ba7dcSVincent Cheng { .compatible = "idt,8a34044" }, 22743a6ba7dcSVincent Cheng { .compatible = "idt,8a34045" }, 22753a6ba7dcSVincent Cheng { .compatible = "idt,8a34046" }, 22763a6ba7dcSVincent Cheng { .compatible = "idt,8a34047" }, 22773a6ba7dcSVincent Cheng { .compatible = "idt,8a34048" }, 22783a6ba7dcSVincent Cheng { .compatible = "idt,8a34049" }, 22793a6ba7dcSVincent Cheng {}, 22803a6ba7dcSVincent Cheng }; 22813a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id); 22823a6ba7dcSVincent Cheng #endif 22833a6ba7dcSVincent Cheng 22843a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = { 22853a6ba7dcSVincent Cheng { "8a34000" }, 22863a6ba7dcSVincent Cheng { "8a34001" }, 22873a6ba7dcSVincent Cheng { "8a34002" }, 22883a6ba7dcSVincent Cheng { "8a34003" }, 22893a6ba7dcSVincent Cheng { "8a34004" }, 22903a6ba7dcSVincent Cheng { "8a34005" }, 22913a6ba7dcSVincent Cheng { "8a34006" }, 22923a6ba7dcSVincent Cheng { "8a34007" }, 22933a6ba7dcSVincent Cheng { "8a34008" }, 22943a6ba7dcSVincent Cheng { "8a34009" }, 22953a6ba7dcSVincent Cheng { "8a34010" }, 22963a6ba7dcSVincent Cheng { "8a34011" }, 22973a6ba7dcSVincent Cheng { "8a34012" }, 22983a6ba7dcSVincent Cheng { "8a34013" }, 22993a6ba7dcSVincent Cheng { "8a34014" }, 23003a6ba7dcSVincent Cheng { "8a34015" }, 23013a6ba7dcSVincent Cheng { "8a34016" }, 23023a6ba7dcSVincent Cheng { "8a34017" }, 23033a6ba7dcSVincent Cheng { "8a34018" }, 23043a6ba7dcSVincent Cheng { "8a34019" }, 23053a6ba7dcSVincent Cheng { "8a34040" }, 23063a6ba7dcSVincent Cheng { "8a34041" }, 23073a6ba7dcSVincent Cheng { "8a34042" }, 23083a6ba7dcSVincent Cheng { "8a34043" }, 23093a6ba7dcSVincent Cheng { "8a34044" }, 23103a6ba7dcSVincent Cheng { "8a34045" }, 23113a6ba7dcSVincent Cheng { "8a34046" }, 23123a6ba7dcSVincent Cheng { "8a34047" }, 23133a6ba7dcSVincent Cheng { "8a34048" }, 23143a6ba7dcSVincent Cheng { "8a34049" }, 23153a6ba7dcSVincent Cheng {}, 23163a6ba7dcSVincent Cheng }; 23173a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id); 23183a6ba7dcSVincent Cheng 23193a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = { 23203a6ba7dcSVincent Cheng .driver = { 23213a6ba7dcSVincent Cheng .of_match_table = of_match_ptr(idtcm_dt_id), 23223a6ba7dcSVincent Cheng .name = "idtcm", 23233a6ba7dcSVincent Cheng }, 23243a6ba7dcSVincent Cheng .probe = idtcm_probe, 23253a6ba7dcSVincent Cheng .remove = idtcm_remove, 23263a6ba7dcSVincent Cheng .id_table = idtcm_i2c_id, 23273a6ba7dcSVincent Cheng }; 23283a6ba7dcSVincent Cheng 23293a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver); 2330