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 int err; 286251f4fe2SMin Li u8 buf[4] = {0}; 287251f4fe2SMin Li 288251f4fe2SMin Li err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 289251f4fe2SMin Li 290251f4fe2SMin Li return err; 291251f4fe2SMin Li } 292251f4fe2SMin Li 293251f4fe2SMin Li static int read_boot_status(struct idtcm *idtcm, u32 *status) 294251f4fe2SMin Li { 295251f4fe2SMin Li int err; 296251f4fe2SMin Li u8 buf[4] = {0}; 297251f4fe2SMin Li 298251f4fe2SMin Li err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf)); 299251f4fe2SMin Li 300251f4fe2SMin Li *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; 301251f4fe2SMin Li 302251f4fe2SMin Li return err; 303251f4fe2SMin Li } 304251f4fe2SMin Li 305251f4fe2SMin Li static int wait_for_boot_status_ready(struct idtcm *idtcm) 306251f4fe2SMin Li { 307251f4fe2SMin Li u32 status = 0; 308251f4fe2SMin Li u8 i = 30; /* 30 * 100ms = 3s */ 309251f4fe2SMin Li int err; 310251f4fe2SMin Li 311251f4fe2SMin Li do { 312251f4fe2SMin Li err = read_boot_status(idtcm, &status); 313251f4fe2SMin Li if (err) 314251f4fe2SMin Li return err; 315251f4fe2SMin Li 316251f4fe2SMin Li if (status == 0xA0) 317251f4fe2SMin Li return 0; 318251f4fe2SMin Li 319251f4fe2SMin Li msleep(100); 320251f4fe2SMin Li i--; 321251f4fe2SMin Li 322251f4fe2SMin Li } while (i); 323251f4fe2SMin Li 3241c49d3e9SVincent Cheng dev_warn(&idtcm->client->dev, "%s timed out", __func__); 325251f4fe2SMin Li 326251f4fe2SMin Li return -EBUSY; 327251f4fe2SMin Li } 328251f4fe2SMin Li 329797d3186SVincent Cheng static int read_sys_apll_status(struct idtcm *idtcm, u8 *status) 330797d3186SVincent Cheng { 331797d3186SVincent Cheng return idtcm_read(idtcm, STATUS, DPLL_SYS_APLL_STATUS, status, 332797d3186SVincent Cheng sizeof(u8)); 333797d3186SVincent Cheng } 334797d3186SVincent Cheng 335797d3186SVincent Cheng static int read_sys_dpll_status(struct idtcm *idtcm, u8 *status) 336797d3186SVincent Cheng { 337797d3186SVincent Cheng return idtcm_read(idtcm, STATUS, DPLL_SYS_STATUS, status, sizeof(u8)); 338797d3186SVincent Cheng } 339797d3186SVincent Cheng 340797d3186SVincent Cheng static int wait_for_sys_apll_dpll_lock(struct idtcm *idtcm) 341797d3186SVincent Cheng { 342797d3186SVincent Cheng unsigned long timeout = jiffies + msecs_to_jiffies(LOCK_TIMEOUT_MS); 343797d3186SVincent Cheng u8 apll = 0; 344797d3186SVincent Cheng u8 dpll = 0; 345797d3186SVincent Cheng int err; 346797d3186SVincent Cheng 347797d3186SVincent Cheng do { 348797d3186SVincent Cheng err = read_sys_apll_status(idtcm, &apll); 349797d3186SVincent Cheng if (err) 350797d3186SVincent Cheng return err; 351797d3186SVincent Cheng 352797d3186SVincent Cheng err = read_sys_dpll_status(idtcm, &dpll); 353797d3186SVincent Cheng if (err) 354797d3186SVincent Cheng return err; 355797d3186SVincent Cheng 356797d3186SVincent Cheng apll &= SYS_APLL_LOSS_LOCK_LIVE_MASK; 357797d3186SVincent Cheng dpll &= DPLL_SYS_STATE_MASK; 358797d3186SVincent Cheng 359797d3186SVincent Cheng if (apll == SYS_APLL_LOSS_LOCK_LIVE_LOCKED && 360797d3186SVincent Cheng dpll == DPLL_STATE_LOCKED) { 361797d3186SVincent Cheng return 0; 362797d3186SVincent Cheng } else if (dpll == DPLL_STATE_FREERUN || 363797d3186SVincent Cheng dpll == DPLL_STATE_HOLDOVER || 364797d3186SVincent Cheng dpll == DPLL_STATE_OPEN_LOOP) { 365797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 366797d3186SVincent Cheng "No wait state: DPLL_SYS_STATE %d", dpll); 367797d3186SVincent Cheng return -EPERM; 368797d3186SVincent Cheng } 369797d3186SVincent Cheng 370797d3186SVincent Cheng msleep(LOCK_POLL_INTERVAL_MS); 371797d3186SVincent Cheng } while (time_is_after_jiffies(timeout)); 372797d3186SVincent Cheng 373797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 374797d3186SVincent Cheng "%d ms lock timeout: SYS APLL Loss Lock %d SYS DPLL state %d", 375797d3186SVincent Cheng LOCK_TIMEOUT_MS, apll, dpll); 376797d3186SVincent Cheng 377797d3186SVincent Cheng return -ETIME; 378797d3186SVincent Cheng } 379797d3186SVincent Cheng 380797d3186SVincent Cheng static void wait_for_chip_ready(struct idtcm *idtcm) 381797d3186SVincent Cheng { 382797d3186SVincent Cheng if (wait_for_boot_status_ready(idtcm)) 383797d3186SVincent Cheng dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0"); 384797d3186SVincent Cheng 385797d3186SVincent Cheng if (wait_for_sys_apll_dpll_lock(idtcm)) 386797d3186SVincent Cheng dev_warn(&idtcm->client->dev, 387797d3186SVincent Cheng "Continuing while SYS APLL/DPLL is not locked"); 388797d3186SVincent Cheng } 389797d3186SVincent Cheng 3903a6ba7dcSVincent Cheng static int _idtcm_gettime(struct idtcm_channel *channel, 3913a6ba7dcSVincent Cheng struct timespec64 *ts) 3923a6ba7dcSVincent Cheng { 3933a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 3943a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 3957ea5fda2SMin Li u8 timeout = 10; 3963a6ba7dcSVincent Cheng u8 trigger; 3973a6ba7dcSVincent Cheng int err; 3983a6ba7dcSVincent Cheng 3993a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 4003a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 4013a6ba7dcSVincent Cheng if (err) 4023a6ba7dcSVincent Cheng return err; 4033a6ba7dcSVincent Cheng 4043a6ba7dcSVincent Cheng trigger &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT); 4053a6ba7dcSVincent Cheng trigger |= (1 << TOD_READ_TRIGGER_SHIFT); 4067ea5fda2SMin Li trigger &= ~TOD_READ_TRIGGER_MODE; /* single shot */ 4073a6ba7dcSVincent Cheng 4083a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_read_primary, 4093a6ba7dcSVincent Cheng TOD_READ_PRIMARY_CMD, &trigger, sizeof(trigger)); 4107ea5fda2SMin Li if (err) 4117ea5fda2SMin Li return err; 4127ea5fda2SMin Li 4137ea5fda2SMin Li /* wait trigger to be 0 */ 4147ea5fda2SMin Li while (trigger & TOD_READ_TRIGGER_MASK) { 4157ea5fda2SMin Li if (idtcm->calculate_overhead_flag) 4167ea5fda2SMin Li idtcm->start_time = ktime_get_raw(); 4177ea5fda2SMin Li 4187ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_read_primary, 4197ea5fda2SMin Li TOD_READ_PRIMARY_CMD, &trigger, 4207ea5fda2SMin Li sizeof(trigger)); 4213a6ba7dcSVincent Cheng if (err) 4223a6ba7dcSVincent Cheng return err; 4233a6ba7dcSVincent Cheng 4247ea5fda2SMin Li if (--timeout == 0) 4257ea5fda2SMin Li return -EIO; 4267ea5fda2SMin Li } 4273a6ba7dcSVincent Cheng 4283a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_read_primary, 4293a6ba7dcSVincent Cheng TOD_READ_PRIMARY, buf, sizeof(buf)); 4303a6ba7dcSVincent Cheng if (err) 4313a6ba7dcSVincent Cheng return err; 4323a6ba7dcSVincent Cheng 4333a6ba7dcSVincent Cheng err = char_array_to_timespec(buf, sizeof(buf), ts); 4343a6ba7dcSVincent Cheng 4353a6ba7dcSVincent Cheng return err; 4363a6ba7dcSVincent Cheng } 4373a6ba7dcSVincent Cheng 4383a6ba7dcSVincent Cheng static int _sync_pll_output(struct idtcm *idtcm, 4393a6ba7dcSVincent Cheng u8 pll, 4403a6ba7dcSVincent Cheng u8 sync_src, 4413a6ba7dcSVincent Cheng u8 qn, 4423a6ba7dcSVincent Cheng u8 qn_plus_1) 4433a6ba7dcSVincent Cheng { 4443a6ba7dcSVincent Cheng int err; 4453a6ba7dcSVincent Cheng u8 val; 4463a6ba7dcSVincent Cheng u16 sync_ctrl0; 4473a6ba7dcSVincent Cheng u16 sync_ctrl1; 4487ea5fda2SMin Li u8 temp; 4493a6ba7dcSVincent Cheng 4503a6ba7dcSVincent Cheng if ((qn == 0) && (qn_plus_1 == 0)) 4513a6ba7dcSVincent Cheng return 0; 4523a6ba7dcSVincent Cheng 4533a6ba7dcSVincent Cheng switch (pll) { 4543a6ba7dcSVincent Cheng case 0: 4553a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q0_Q1_CH_SYNC_CTRL_0; 4563a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q0_Q1_CH_SYNC_CTRL_1; 4573a6ba7dcSVincent Cheng break; 4583a6ba7dcSVincent Cheng case 1: 4593a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q2_Q3_CH_SYNC_CTRL_0; 4603a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q2_Q3_CH_SYNC_CTRL_1; 4613a6ba7dcSVincent Cheng break; 4623a6ba7dcSVincent Cheng case 2: 4633a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q4_Q5_CH_SYNC_CTRL_0; 4643a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q4_Q5_CH_SYNC_CTRL_1; 4653a6ba7dcSVincent Cheng break; 4663a6ba7dcSVincent Cheng case 3: 4673a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q6_Q7_CH_SYNC_CTRL_0; 4683a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q6_Q7_CH_SYNC_CTRL_1; 4693a6ba7dcSVincent Cheng break; 4703a6ba7dcSVincent Cheng case 4: 4713a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q8_CH_SYNC_CTRL_0; 4723a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q8_CH_SYNC_CTRL_1; 4733a6ba7dcSVincent Cheng break; 4743a6ba7dcSVincent Cheng case 5: 4753a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q9_CH_SYNC_CTRL_0; 4763a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q9_CH_SYNC_CTRL_1; 4773a6ba7dcSVincent Cheng break; 4783a6ba7dcSVincent Cheng case 6: 4793a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q10_CH_SYNC_CTRL_0; 4803a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q10_CH_SYNC_CTRL_1; 4813a6ba7dcSVincent Cheng break; 4823a6ba7dcSVincent Cheng case 7: 4833a6ba7dcSVincent Cheng sync_ctrl0 = HW_Q11_CH_SYNC_CTRL_0; 4843a6ba7dcSVincent Cheng sync_ctrl1 = HW_Q11_CH_SYNC_CTRL_1; 4853a6ba7dcSVincent Cheng break; 4863a6ba7dcSVincent Cheng default: 4873a6ba7dcSVincent Cheng return -EINVAL; 4883a6ba7dcSVincent Cheng } 4893a6ba7dcSVincent Cheng 4903a6ba7dcSVincent Cheng val = SYNCTRL1_MASTER_SYNC_RST; 4913a6ba7dcSVincent Cheng 4923a6ba7dcSVincent Cheng /* Place master sync in reset */ 4933a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 4943a6ba7dcSVincent Cheng if (err) 4953a6ba7dcSVincent Cheng return err; 4963a6ba7dcSVincent Cheng 4973a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl0, &sync_src, sizeof(sync_src)); 4983a6ba7dcSVincent Cheng if (err) 4993a6ba7dcSVincent Cheng return err; 5003a6ba7dcSVincent Cheng 5013a6ba7dcSVincent Cheng /* Set sync trigger mask */ 5023a6ba7dcSVincent Cheng val |= SYNCTRL1_FBDIV_FRAME_SYNC_TRIG | SYNCTRL1_FBDIV_SYNC_TRIG; 5033a6ba7dcSVincent Cheng 5043a6ba7dcSVincent Cheng if (qn) 5053a6ba7dcSVincent Cheng val |= SYNCTRL1_Q0_DIV_SYNC_TRIG; 5063a6ba7dcSVincent Cheng 5073a6ba7dcSVincent Cheng if (qn_plus_1) 5083a6ba7dcSVincent Cheng val |= SYNCTRL1_Q1_DIV_SYNC_TRIG; 5093a6ba7dcSVincent Cheng 5103a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 5113a6ba7dcSVincent Cheng if (err) 5123a6ba7dcSVincent Cheng return err; 5133a6ba7dcSVincent Cheng 5147ea5fda2SMin Li /* PLL5 can have OUT8 as second additional output. */ 5157ea5fda2SMin Li if ((pll == 5) && (qn_plus_1 != 0)) { 5167ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 5177ea5fda2SMin Li &temp, sizeof(temp)); 5187ea5fda2SMin Li if (err) 5197ea5fda2SMin Li return err; 5207ea5fda2SMin Li 5217ea5fda2SMin Li temp &= ~(Q9_TO_Q8_SYNC_TRIG); 5227ea5fda2SMin Li 5237ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 5247ea5fda2SMin Li &temp, sizeof(temp)); 5257ea5fda2SMin Li if (err) 5267ea5fda2SMin Li return err; 5277ea5fda2SMin Li 5287ea5fda2SMin Li temp |= Q9_TO_Q8_SYNC_TRIG; 5297ea5fda2SMin Li 5307ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q8_CTRL_SPARE, 5317ea5fda2SMin Li &temp, sizeof(temp)); 5327ea5fda2SMin Li if (err) 5337ea5fda2SMin Li return err; 5347ea5fda2SMin Li } 5357ea5fda2SMin Li 5367ea5fda2SMin Li /* PLL6 can have OUT11 as second additional output. */ 5377ea5fda2SMin Li if ((pll == 6) && (qn_plus_1 != 0)) { 5387ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 5397ea5fda2SMin Li &temp, sizeof(temp)); 5407ea5fda2SMin Li if (err) 5417ea5fda2SMin Li return err; 5427ea5fda2SMin Li 5437ea5fda2SMin Li temp &= ~(Q10_TO_Q11_SYNC_TRIG); 5447ea5fda2SMin Li 5457ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 5467ea5fda2SMin Li &temp, sizeof(temp)); 5477ea5fda2SMin Li if (err) 5487ea5fda2SMin Li return err; 5497ea5fda2SMin Li 5507ea5fda2SMin Li temp |= Q10_TO_Q11_SYNC_TRIG; 5517ea5fda2SMin Li 5527ea5fda2SMin Li err = idtcm_write(idtcm, 0, HW_Q11_CTRL_SPARE, 5537ea5fda2SMin Li &temp, sizeof(temp)); 5547ea5fda2SMin Li if (err) 5557ea5fda2SMin Li return err; 5567ea5fda2SMin Li } 5577ea5fda2SMin Li 5583a6ba7dcSVincent Cheng /* Place master sync out of reset */ 5593a6ba7dcSVincent Cheng val &= ~(SYNCTRL1_MASTER_SYNC_RST); 5603a6ba7dcSVincent Cheng err = idtcm_write(idtcm, 0, sync_ctrl1, &val, sizeof(val)); 5613a6ba7dcSVincent Cheng 5623a6ba7dcSVincent Cheng return err; 5633a6ba7dcSVincent Cheng } 5643a6ba7dcSVincent Cheng 5657ea5fda2SMin Li static int sync_source_dpll_tod_pps(u16 tod_addr, u8 *sync_src) 5667ea5fda2SMin Li { 5677ea5fda2SMin Li int err = 0; 5687ea5fda2SMin Li 5697ea5fda2SMin Li switch (tod_addr) { 5707ea5fda2SMin Li case TOD_0: 5717ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL0_TOD_PPS; 5727ea5fda2SMin Li break; 5737ea5fda2SMin Li case TOD_1: 5747ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL1_TOD_PPS; 5757ea5fda2SMin Li break; 5767ea5fda2SMin Li case TOD_2: 5777ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL2_TOD_PPS; 5787ea5fda2SMin Li break; 5797ea5fda2SMin Li case TOD_3: 5807ea5fda2SMin Li *sync_src = SYNC_SOURCE_DPLL3_TOD_PPS; 5817ea5fda2SMin Li break; 5827ea5fda2SMin Li default: 5837ea5fda2SMin Li err = -EINVAL; 5847ea5fda2SMin Li } 5857ea5fda2SMin Li 5867ea5fda2SMin Li return err; 5877ea5fda2SMin Li } 5887ea5fda2SMin Li 5893a6ba7dcSVincent Cheng static int idtcm_sync_pps_output(struct idtcm_channel *channel) 5903a6ba7dcSVincent Cheng { 5913a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 5923a6ba7dcSVincent Cheng u8 pll; 5933a6ba7dcSVincent Cheng u8 sync_src; 5943a6ba7dcSVincent Cheng u8 qn; 5953a6ba7dcSVincent Cheng u8 qn_plus_1; 5963a6ba7dcSVincent Cheng int err = 0; 5977ea5fda2SMin Li u8 out8_mux = 0; 5987ea5fda2SMin Li u8 out11_mux = 0; 5997ea5fda2SMin Li u8 temp; 6003a6ba7dcSVincent Cheng u16 output_mask = channel->output_mask; 6013a6ba7dcSVincent Cheng 6027ea5fda2SMin Li err = sync_source_dpll_tod_pps(channel->tod_n, &sync_src); 6037ea5fda2SMin Li if (err) 6047ea5fda2SMin Li return err; 6057ea5fda2SMin Li 6067ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, 6077ea5fda2SMin Li &temp, sizeof(temp)); 6087ea5fda2SMin Li if (err) 6097ea5fda2SMin Li return err; 6107ea5fda2SMin Li 6117ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 6127ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 6137ea5fda2SMin Li out8_mux = 1; 6147ea5fda2SMin Li 6157ea5fda2SMin Li err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, 6167ea5fda2SMin Li &temp, sizeof(temp)); 6177ea5fda2SMin Li if (err) 6187ea5fda2SMin Li return err; 6197ea5fda2SMin Li 6207ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 6217ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 6227ea5fda2SMin Li out11_mux = 1; 6233a6ba7dcSVincent Cheng 6243a6ba7dcSVincent Cheng for (pll = 0; pll < 8; pll++) { 6257ea5fda2SMin Li qn = 0; 6267ea5fda2SMin Li qn_plus_1 = 0; 6273a6ba7dcSVincent Cheng 6283a6ba7dcSVincent Cheng if (pll < 4) { 6293a6ba7dcSVincent Cheng /* First 4 pll has 2 outputs */ 6307ea5fda2SMin Li qn = output_mask & 0x1; 6317ea5fda2SMin Li output_mask = output_mask >> 1; 6323a6ba7dcSVincent Cheng qn_plus_1 = output_mask & 0x1; 6333a6ba7dcSVincent Cheng output_mask = output_mask >> 1; 6347ea5fda2SMin Li } else if (pll == 4) { 6357ea5fda2SMin Li if (out8_mux == 0) { 6367ea5fda2SMin Li qn = output_mask & 0x1; 6377ea5fda2SMin Li output_mask = output_mask >> 1; 6387ea5fda2SMin Li } 6397ea5fda2SMin Li } else if (pll == 5) { 6407ea5fda2SMin Li if (out8_mux) { 6417ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 6427ea5fda2SMin Li output_mask = output_mask >> 1; 6437ea5fda2SMin Li } 6447ea5fda2SMin Li qn = output_mask & 0x1; 6457ea5fda2SMin Li output_mask = output_mask >> 1; 6467ea5fda2SMin Li } else if (pll == 6) { 6477ea5fda2SMin Li qn = output_mask & 0x1; 6487ea5fda2SMin Li output_mask = output_mask >> 1; 6497ea5fda2SMin Li if (out11_mux) { 6507ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 6517ea5fda2SMin Li output_mask = output_mask >> 1; 6527ea5fda2SMin Li } 6537ea5fda2SMin Li } else if (pll == 7) { 6547ea5fda2SMin Li if (out11_mux == 0) { 6557ea5fda2SMin Li qn = output_mask & 0x1; 6567ea5fda2SMin Li output_mask = output_mask >> 1; 6577ea5fda2SMin Li } 6583a6ba7dcSVincent Cheng } 6593a6ba7dcSVincent Cheng 6603a6ba7dcSVincent Cheng if ((qn != 0) || (qn_plus_1 != 0)) 6613a6ba7dcSVincent Cheng err = _sync_pll_output(idtcm, pll, sync_src, qn, 6623a6ba7dcSVincent Cheng qn_plus_1); 6633a6ba7dcSVincent Cheng 6643a6ba7dcSVincent Cheng if (err) 6653a6ba7dcSVincent Cheng return err; 6663a6ba7dcSVincent Cheng } 6673a6ba7dcSVincent Cheng 6683a6ba7dcSVincent Cheng return err; 6693a6ba7dcSVincent Cheng } 6703a6ba7dcSVincent Cheng 6717ea5fda2SMin Li static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel, 6723a6ba7dcSVincent Cheng struct timespec64 const *ts, 6733a6ba7dcSVincent Cheng enum hw_tod_write_trig_sel wr_trig) 6743a6ba7dcSVincent Cheng { 6753a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 6763a6ba7dcSVincent Cheng u8 buf[TOD_BYTE_COUNT]; 6773a6ba7dcSVincent Cheng u8 cmd; 6783a6ba7dcSVincent Cheng int err; 6793a6ba7dcSVincent Cheng struct timespec64 local_ts = *ts; 6803a6ba7dcSVincent Cheng s64 total_overhead_ns; 6813a6ba7dcSVincent Cheng 6823a6ba7dcSVincent Cheng /* Configure HW TOD write trigger. */ 6833a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6843a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6853a6ba7dcSVincent Cheng if (err) 6863a6ba7dcSVincent Cheng return err; 6873a6ba7dcSVincent Cheng 6883a6ba7dcSVincent Cheng cmd &= ~(0x0f); 6893a6ba7dcSVincent Cheng cmd |= wr_trig | 0x08; 6903a6ba7dcSVincent Cheng 6913a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 6923a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 6933a6ba7dcSVincent Cheng if (err) 6943a6ba7dcSVincent Cheng return err; 6953a6ba7dcSVincent Cheng 6963a6ba7dcSVincent Cheng if (wr_trig != HW_TOD_WR_TRIG_SEL_MSB) { 6973a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 6983a6ba7dcSVincent Cheng if (err) 6993a6ba7dcSVincent Cheng return err; 7003a6ba7dcSVincent Cheng 7013a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 7023a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 7033a6ba7dcSVincent Cheng if (err) 7043a6ba7dcSVincent Cheng return err; 7053a6ba7dcSVincent Cheng } 7063a6ba7dcSVincent Cheng 7073a6ba7dcSVincent Cheng /* ARM HW TOD write trigger. */ 7083a6ba7dcSVincent Cheng cmd &= ~(0x08); 7093a6ba7dcSVincent Cheng 7103a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_CTRL_1, 7113a6ba7dcSVincent Cheng &cmd, sizeof(cmd)); 7123a6ba7dcSVincent Cheng 7133a6ba7dcSVincent Cheng if (wr_trig == HW_TOD_WR_TRIG_SEL_MSB) { 7143a6ba7dcSVincent Cheng if (idtcm->calculate_overhead_flag) { 7151ece2fbeSVincent Cheng /* Assumption: I2C @ 400KHz */ 7167260d1c8SMin Li ktime_t diff = ktime_sub(ktime_get_raw(), 7177260d1c8SMin Li idtcm->start_time); 7187260d1c8SMin Li total_overhead_ns = ktime_to_ns(diff) 7193a6ba7dcSVincent Cheng + idtcm->tod_write_overhead_ns 7203a6ba7dcSVincent Cheng + SETTIME_CORRECTION; 7213a6ba7dcSVincent Cheng 7223a6ba7dcSVincent Cheng timespec64_add_ns(&local_ts, total_overhead_ns); 7233a6ba7dcSVincent Cheng 7243a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 7253a6ba7dcSVincent Cheng } 7263a6ba7dcSVincent Cheng 7273a6ba7dcSVincent Cheng err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 7283a6ba7dcSVincent Cheng if (err) 7293a6ba7dcSVincent Cheng return err; 7303a6ba7dcSVincent Cheng 7313a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->hw_dpll_n, 7323a6ba7dcSVincent Cheng HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 7333a6ba7dcSVincent Cheng } 7343a6ba7dcSVincent Cheng 7353a6ba7dcSVincent Cheng return err; 7363a6ba7dcSVincent Cheng } 7373a6ba7dcSVincent Cheng 7387ea5fda2SMin Li static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel, 7397ea5fda2SMin Li struct timespec64 const *ts, 7407ea5fda2SMin Li enum scsr_tod_write_trig_sel wr_trig, 7417ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 7427ea5fda2SMin Li { 7437ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 7447ea5fda2SMin Li unsigned char buf[TOD_BYTE_COUNT], cmd; 7457ea5fda2SMin Li struct timespec64 local_ts = *ts; 7467ea5fda2SMin Li int err, count = 0; 7477ea5fda2SMin Li 7487ea5fda2SMin Li timespec64_add_ns(&local_ts, SETTIME_CORRECTION); 7497ea5fda2SMin Li 7507ea5fda2SMin Li err = timespec_to_char_array(&local_ts, buf, sizeof(buf)); 7517ea5fda2SMin Li if (err) 7527ea5fda2SMin Li return err; 7537ea5fda2SMin Li 7547ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE, 7557ea5fda2SMin Li buf, sizeof(buf)); 7567ea5fda2SMin Li if (err) 7577ea5fda2SMin Li return err; 7587ea5fda2SMin Li 7597ea5fda2SMin Li /* Trigger the write operation. */ 7607ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7617ea5fda2SMin Li &cmd, sizeof(cmd)); 7627ea5fda2SMin Li if (err) 7637ea5fda2SMin Li return err; 7647ea5fda2SMin Li 7657ea5fda2SMin Li cmd &= ~(TOD_WRITE_SELECTION_MASK << TOD_WRITE_SELECTION_SHIFT); 7667ea5fda2SMin Li cmd &= ~(TOD_WRITE_TYPE_MASK << TOD_WRITE_TYPE_SHIFT); 7677ea5fda2SMin Li cmd |= (wr_trig << TOD_WRITE_SELECTION_SHIFT); 7687ea5fda2SMin Li cmd |= (wr_type << TOD_WRITE_TYPE_SHIFT); 7697ea5fda2SMin Li 7707ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_write, TOD_WRITE_CMD, 7717ea5fda2SMin Li &cmd, sizeof(cmd)); 7727ea5fda2SMin Li if (err) 7737ea5fda2SMin Li return err; 7747ea5fda2SMin Li 7757ea5fda2SMin Li /* Wait for the operation to complete. */ 7767ea5fda2SMin Li while (1) { 7777ea5fda2SMin Li /* pps trigger takes up to 1 sec to complete */ 7787ea5fda2SMin Li if (wr_trig == SCSR_TOD_WR_TRIG_SEL_TODPPS) 7797ea5fda2SMin Li msleep(50); 7807ea5fda2SMin Li 7817ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_write, TOD_WRITE_CMD, 7827ea5fda2SMin Li &cmd, sizeof(cmd)); 7837ea5fda2SMin Li if (err) 7847ea5fda2SMin Li return err; 7857ea5fda2SMin Li 786251f4fe2SMin Li if ((cmd & TOD_WRITE_SELECTION_MASK) == 0) 7877ea5fda2SMin Li break; 7887ea5fda2SMin Li 7897ea5fda2SMin Li if (++count > 20) { 7907ea5fda2SMin Li dev_err(&idtcm->client->dev, 7911c49d3e9SVincent Cheng "Timed out waiting for the write counter"); 7927ea5fda2SMin Li return -EIO; 7937ea5fda2SMin Li } 7947ea5fda2SMin Li } 7957ea5fda2SMin Li 7967ea5fda2SMin Li return 0; 7977ea5fda2SMin Li } 7987ea5fda2SMin Li 7997260d1c8SMin Li static int get_output_base_addr(u8 outn) 8007260d1c8SMin Li { 8017260d1c8SMin Li int base; 8027260d1c8SMin Li 8037260d1c8SMin Li switch (outn) { 8047260d1c8SMin Li case 0: 8057260d1c8SMin Li base = OUTPUT_0; 8067260d1c8SMin Li break; 8077260d1c8SMin Li case 1: 8087260d1c8SMin Li base = OUTPUT_1; 8097260d1c8SMin Li break; 8107260d1c8SMin Li case 2: 8117260d1c8SMin Li base = OUTPUT_2; 8127260d1c8SMin Li break; 8137260d1c8SMin Li case 3: 8147260d1c8SMin Li base = OUTPUT_3; 8157260d1c8SMin Li break; 8167260d1c8SMin Li case 4: 8177260d1c8SMin Li base = OUTPUT_4; 8187260d1c8SMin Li break; 8197260d1c8SMin Li case 5: 8207260d1c8SMin Li base = OUTPUT_5; 8217260d1c8SMin Li break; 8227260d1c8SMin Li case 6: 8237260d1c8SMin Li base = OUTPUT_6; 8247260d1c8SMin Li break; 8257260d1c8SMin Li case 7: 8267260d1c8SMin Li base = OUTPUT_7; 8277260d1c8SMin Li break; 8287260d1c8SMin Li case 8: 8297260d1c8SMin Li base = OUTPUT_8; 8307260d1c8SMin Li break; 8317260d1c8SMin Li case 9: 8327260d1c8SMin Li base = OUTPUT_9; 8337260d1c8SMin Li break; 8347260d1c8SMin Li case 10: 8357260d1c8SMin Li base = OUTPUT_10; 8367260d1c8SMin Li break; 8377260d1c8SMin Li case 11: 8387260d1c8SMin Li base = OUTPUT_11; 8397260d1c8SMin Li break; 8407260d1c8SMin Li default: 8417260d1c8SMin Li base = -EINVAL; 8427260d1c8SMin Li } 8437260d1c8SMin Li 8447260d1c8SMin Li return base; 8457260d1c8SMin Li } 8467260d1c8SMin Li 847da948233SMin Li static int _idtcm_settime_deprecated(struct idtcm_channel *channel, 848251f4fe2SMin Li struct timespec64 const *ts) 8493a6ba7dcSVincent Cheng { 8503a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8513a6ba7dcSVincent Cheng int err; 8523a6ba7dcSVincent Cheng 853251f4fe2SMin Li err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB); 8547ea5fda2SMin Li if (err) { 8557ea5fda2SMin Li dev_err(&idtcm->client->dev, 8561c49d3e9SVincent Cheng "%s: Set HW ToD failed", __func__); 8573a6ba7dcSVincent Cheng return err; 8587ea5fda2SMin Li } 8593a6ba7dcSVincent Cheng 8607ea5fda2SMin Li return idtcm_sync_pps_output(channel); 8617ea5fda2SMin Li } 8623a6ba7dcSVincent Cheng 863da948233SMin Li static int _idtcm_settime(struct idtcm_channel *channel, 8647ea5fda2SMin Li struct timespec64 const *ts, 8657ea5fda2SMin Li enum scsr_tod_write_type_sel wr_type) 8667ea5fda2SMin Li { 8677ea5fda2SMin Li return _idtcm_set_dpll_scsr_tod(channel, ts, 8687ea5fda2SMin Li SCSR_TOD_WR_TRIG_SEL_IMMEDIATE, 8697ea5fda2SMin Li wr_type); 8703a6ba7dcSVincent Cheng } 8713a6ba7dcSVincent Cheng 8723a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_offset(struct idtcm_channel *channel, 8733a6ba7dcSVincent Cheng s32 offset_ns) 8743a6ba7dcSVincent Cheng { 8753a6ba7dcSVincent Cheng int err; 8763a6ba7dcSVincent Cheng int i; 8773a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8783a6ba7dcSVincent Cheng u8 buf[4]; 8793a6ba7dcSVincent Cheng 8803a6ba7dcSVincent Cheng for (i = 0; i < 4; i++) { 8813a6ba7dcSVincent Cheng buf[i] = 0xff & (offset_ns); 8823a6ba7dcSVincent Cheng offset_ns >>= 8; 8833a6ba7dcSVincent Cheng } 8843a6ba7dcSVincent Cheng 8853a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, PULL_IN_OFFSET, 8863a6ba7dcSVincent Cheng buf, sizeof(buf)); 8873a6ba7dcSVincent Cheng 8883a6ba7dcSVincent Cheng return err; 8893a6ba7dcSVincent Cheng } 8903a6ba7dcSVincent Cheng 8913a6ba7dcSVincent Cheng static int idtcm_set_phase_pull_in_slope_limit(struct idtcm_channel *channel, 8923a6ba7dcSVincent Cheng u32 max_ffo_ppb) 8933a6ba7dcSVincent Cheng { 8943a6ba7dcSVincent Cheng int err; 8953a6ba7dcSVincent Cheng u8 i; 8963a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 8973a6ba7dcSVincent Cheng u8 buf[3]; 8983a6ba7dcSVincent Cheng 8993a6ba7dcSVincent Cheng if (max_ffo_ppb & 0xff000000) 9003a6ba7dcSVincent Cheng max_ffo_ppb = 0; 9013a6ba7dcSVincent Cheng 9023a6ba7dcSVincent Cheng for (i = 0; i < 3; i++) { 9033a6ba7dcSVincent Cheng buf[i] = 0xff & (max_ffo_ppb); 9043a6ba7dcSVincent Cheng max_ffo_ppb >>= 8; 9053a6ba7dcSVincent Cheng } 9063a6ba7dcSVincent Cheng 9073a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 9083a6ba7dcSVincent Cheng PULL_IN_SLOPE_LIMIT, buf, sizeof(buf)); 9093a6ba7dcSVincent Cheng 9103a6ba7dcSVincent Cheng return err; 9113a6ba7dcSVincent Cheng } 9123a6ba7dcSVincent Cheng 9133a6ba7dcSVincent Cheng static int idtcm_start_phase_pull_in(struct idtcm_channel *channel) 9143a6ba7dcSVincent Cheng { 9153a6ba7dcSVincent Cheng int err; 9163a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 9173a6ba7dcSVincent Cheng u8 buf; 9183a6ba7dcSVincent Cheng 9193a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_phase_pull_in, PULL_IN_CTRL, 9203a6ba7dcSVincent Cheng &buf, sizeof(buf)); 9213a6ba7dcSVincent Cheng if (err) 9223a6ba7dcSVincent Cheng return err; 9233a6ba7dcSVincent Cheng 9243a6ba7dcSVincent Cheng if (buf == 0) { 9253a6ba7dcSVincent Cheng buf = 0x01; 9263a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase_pull_in, 9273a6ba7dcSVincent Cheng PULL_IN_CTRL, &buf, sizeof(buf)); 9283a6ba7dcSVincent Cheng } else { 9293a6ba7dcSVincent Cheng err = -EBUSY; 9303a6ba7dcSVincent Cheng } 9313a6ba7dcSVincent Cheng 9323a6ba7dcSVincent Cheng return err; 9333a6ba7dcSVincent Cheng } 9343a6ba7dcSVincent Cheng 9353a6ba7dcSVincent Cheng static int idtcm_do_phase_pull_in(struct idtcm_channel *channel, 9363a6ba7dcSVincent Cheng s32 offset_ns, 9373a6ba7dcSVincent Cheng u32 max_ffo_ppb) 9383a6ba7dcSVincent Cheng { 9393a6ba7dcSVincent Cheng int err; 9403a6ba7dcSVincent Cheng 9413a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_offset(channel, -offset_ns); 9423a6ba7dcSVincent Cheng if (err) 9433a6ba7dcSVincent Cheng return err; 9443a6ba7dcSVincent Cheng 9453a6ba7dcSVincent Cheng err = idtcm_set_phase_pull_in_slope_limit(channel, max_ffo_ppb); 9463a6ba7dcSVincent Cheng if (err) 9473a6ba7dcSVincent Cheng return err; 9483a6ba7dcSVincent Cheng 9493a6ba7dcSVincent Cheng err = idtcm_start_phase_pull_in(channel); 9503a6ba7dcSVincent Cheng 9513a6ba7dcSVincent Cheng return err; 9523a6ba7dcSVincent Cheng } 9533a6ba7dcSVincent Cheng 9547ea5fda2SMin Li static int set_tod_write_overhead(struct idtcm_channel *channel) 9557ea5fda2SMin Li { 9567ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 9577ea5fda2SMin Li s64 current_ns = 0; 9587ea5fda2SMin Li s64 lowest_ns = 0; 9597ea5fda2SMin Li int err; 9607ea5fda2SMin Li u8 i; 9617ea5fda2SMin Li ktime_t start; 9627ea5fda2SMin Li ktime_t stop; 9637260d1c8SMin Li ktime_t diff; 9647ea5fda2SMin Li 9657ea5fda2SMin Li char buf[TOD_BYTE_COUNT] = {0}; 9667ea5fda2SMin Li 9677ea5fda2SMin Li /* Set page offset */ 9687ea5fda2SMin Li idtcm_write(idtcm, channel->hw_dpll_n, HW_DPLL_TOD_OVR__0, 9697ea5fda2SMin Li buf, sizeof(buf)); 9707ea5fda2SMin Li 9717ea5fda2SMin Li for (i = 0; i < TOD_WRITE_OVERHEAD_COUNT_MAX; i++) { 9727ea5fda2SMin Li start = ktime_get_raw(); 9737ea5fda2SMin Li 9747ea5fda2SMin Li err = idtcm_write(idtcm, channel->hw_dpll_n, 9757ea5fda2SMin Li HW_DPLL_TOD_OVR__0, buf, sizeof(buf)); 9767ea5fda2SMin Li if (err) 9777ea5fda2SMin Li return err; 9787ea5fda2SMin Li 9797ea5fda2SMin Li stop = ktime_get_raw(); 9807ea5fda2SMin Li 9817260d1c8SMin Li diff = ktime_sub(stop, start); 9827260d1c8SMin Li 9837260d1c8SMin Li current_ns = ktime_to_ns(diff); 9847ea5fda2SMin Li 9857ea5fda2SMin Li if (i == 0) { 9867ea5fda2SMin Li lowest_ns = current_ns; 9877ea5fda2SMin Li } else { 9887ea5fda2SMin Li if (current_ns < lowest_ns) 9897ea5fda2SMin Li lowest_ns = current_ns; 9907ea5fda2SMin Li } 9917ea5fda2SMin Li } 9927ea5fda2SMin Li 9937ea5fda2SMin Li idtcm->tod_write_overhead_ns = lowest_ns; 9947ea5fda2SMin Li 9957ea5fda2SMin Li return err; 9967ea5fda2SMin Li } 9977ea5fda2SMin Li 998da948233SMin Li static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta) 9993a6ba7dcSVincent Cheng { 10003a6ba7dcSVincent Cheng int err; 10013a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 10023a6ba7dcSVincent Cheng struct timespec64 ts; 10033a6ba7dcSVincent Cheng s64 now; 10043a6ba7dcSVincent Cheng 1005da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) { 10063a6ba7dcSVincent Cheng err = idtcm_do_phase_pull_in(channel, delta, 0); 10073a6ba7dcSVincent Cheng } else { 10083a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 1; 10093a6ba7dcSVincent Cheng 10107ea5fda2SMin Li err = set_tod_write_overhead(channel); 10117ea5fda2SMin Li if (err) 10127ea5fda2SMin Li return err; 10137ea5fda2SMin Li 10143a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, &ts); 10153a6ba7dcSVincent Cheng if (err) 10163a6ba7dcSVincent Cheng return err; 10173a6ba7dcSVincent Cheng 10183a6ba7dcSVincent Cheng now = timespec64_to_ns(&ts); 10193a6ba7dcSVincent Cheng now += delta; 10203a6ba7dcSVincent Cheng 10213a6ba7dcSVincent Cheng ts = ns_to_timespec64(now); 10223a6ba7dcSVincent Cheng 1023da948233SMin Li err = _idtcm_settime_deprecated(channel, &ts); 10243a6ba7dcSVincent Cheng } 10253a6ba7dcSVincent Cheng 10263a6ba7dcSVincent Cheng return err; 10273a6ba7dcSVincent Cheng } 10283a6ba7dcSVincent Cheng 10293a6ba7dcSVincent Cheng static int idtcm_state_machine_reset(struct idtcm *idtcm) 10303a6ba7dcSVincent Cheng { 10313a6ba7dcSVincent Cheng u8 byte = SM_RESET_CMD; 1032251f4fe2SMin Li u32 status = 0; 1033251f4fe2SMin Li int err; 1034251f4fe2SMin Li u8 i; 1035251f4fe2SMin Li 1036251f4fe2SMin Li clear_boot_status(idtcm); 10373a6ba7dcSVincent Cheng 10383a6ba7dcSVincent Cheng err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte)); 10393a6ba7dcSVincent Cheng 1040251f4fe2SMin Li if (!err) { 1041251f4fe2SMin Li for (i = 0; i < 30; i++) { 1042251f4fe2SMin Li msleep_interruptible(100); 1043251f4fe2SMin Li read_boot_status(idtcm, &status); 1044251f4fe2SMin Li 1045251f4fe2SMin Li if (status == 0xA0) { 1046251f4fe2SMin Li dev_dbg(&idtcm->client->dev, 10471c49d3e9SVincent Cheng "SM_RESET completed in %d ms", i * 100); 1048251f4fe2SMin Li break; 1049251f4fe2SMin Li } 1050251f4fe2SMin Li } 1051251f4fe2SMin Li 1052251f4fe2SMin Li if (!status) 10531c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, 10541c49d3e9SVincent Cheng "Timed out waiting for CM_RESET to complete"); 1055251f4fe2SMin Li } 10563a6ba7dcSVincent Cheng 10573a6ba7dcSVincent Cheng return err; 10583a6ba7dcSVincent Cheng } 10593a6ba7dcSVincent Cheng 10603a6ba7dcSVincent Cheng static int idtcm_read_hw_rev_id(struct idtcm *idtcm, u8 *hw_rev_id) 10613a6ba7dcSVincent Cheng { 10621ece2fbeSVincent Cheng return idtcm_read(idtcm, HW_REVISION, REV_ID, hw_rev_id, sizeof(u8)); 10633a6ba7dcSVincent Cheng } 10643a6ba7dcSVincent Cheng 10653a6ba7dcSVincent Cheng static int idtcm_read_product_id(struct idtcm *idtcm, u16 *product_id) 10663a6ba7dcSVincent Cheng { 10673a6ba7dcSVincent Cheng int err; 10683a6ba7dcSVincent Cheng u8 buf[2] = {0}; 10693a6ba7dcSVincent Cheng 10703a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, PRODUCT_ID, buf, sizeof(buf)); 10713a6ba7dcSVincent Cheng 10723a6ba7dcSVincent Cheng *product_id = (buf[1] << 8) | buf[0]; 10733a6ba7dcSVincent Cheng 10743a6ba7dcSVincent Cheng return err; 10753a6ba7dcSVincent Cheng } 10763a6ba7dcSVincent Cheng 10773a6ba7dcSVincent Cheng static int idtcm_read_major_release(struct idtcm *idtcm, u8 *major) 10783a6ba7dcSVincent Cheng { 10793a6ba7dcSVincent Cheng int err; 10803a6ba7dcSVincent Cheng u8 buf = 0; 10813a6ba7dcSVincent Cheng 10823a6ba7dcSVincent Cheng err = idtcm_read(idtcm, GENERAL_STATUS, MAJ_REL, &buf, sizeof(buf)); 10833a6ba7dcSVincent Cheng 10843a6ba7dcSVincent Cheng *major = buf >> 1; 10853a6ba7dcSVincent Cheng 10863a6ba7dcSVincent Cheng return err; 10873a6ba7dcSVincent Cheng } 10883a6ba7dcSVincent Cheng 10893a6ba7dcSVincent Cheng static int idtcm_read_minor_release(struct idtcm *idtcm, u8 *minor) 10903a6ba7dcSVincent Cheng { 10913a6ba7dcSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, MIN_REL, minor, sizeof(u8)); 10923a6ba7dcSVincent Cheng } 10933a6ba7dcSVincent Cheng 10943a6ba7dcSVincent Cheng static int idtcm_read_hotfix_release(struct idtcm *idtcm, u8 *hotfix) 10953a6ba7dcSVincent Cheng { 10963a6ba7dcSVincent Cheng return idtcm_read(idtcm, 10973a6ba7dcSVincent Cheng GENERAL_STATUS, 10983a6ba7dcSVincent Cheng HOTFIX_REL, 10993a6ba7dcSVincent Cheng hotfix, 11003a6ba7dcSVincent Cheng sizeof(u8)); 11013a6ba7dcSVincent Cheng } 11023a6ba7dcSVincent Cheng 11031ece2fbeSVincent Cheng static int idtcm_read_otp_scsr_config_select(struct idtcm *idtcm, 11041ece2fbeSVincent Cheng u8 *config_select) 11053a6ba7dcSVincent Cheng { 11061ece2fbeSVincent Cheng return idtcm_read(idtcm, GENERAL_STATUS, OTP_SCSR_CONFIG_SELECT, 11071ece2fbeSVincent Cheng config_select, sizeof(u8)); 11083a6ba7dcSVincent Cheng } 11093a6ba7dcSVincent Cheng 11103a6ba7dcSVincent Cheng static int set_pll_output_mask(struct idtcm *idtcm, u16 addr, u8 val) 11113a6ba7dcSVincent Cheng { 11123a6ba7dcSVincent Cheng int err = 0; 11133a6ba7dcSVincent Cheng 11143a6ba7dcSVincent Cheng switch (addr) { 11157ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR: 11163a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[0].output_mask, val); 11173a6ba7dcSVincent Cheng break; 11187ea5fda2SMin Li case TOD0_OUT_ALIGN_MASK_ADDR + 1: 11193a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[0].output_mask, val); 11203a6ba7dcSVincent Cheng break; 11217ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR: 11223a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[1].output_mask, val); 11233a6ba7dcSVincent Cheng break; 11247ea5fda2SMin Li case TOD1_OUT_ALIGN_MASK_ADDR + 1: 11253a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[1].output_mask, val); 11263a6ba7dcSVincent Cheng break; 11277ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR: 11283a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[2].output_mask, val); 11293a6ba7dcSVincent Cheng break; 11307ea5fda2SMin Li case TOD2_OUT_ALIGN_MASK_ADDR + 1: 11313a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[2].output_mask, val); 11323a6ba7dcSVincent Cheng break; 11337ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR: 11343a6ba7dcSVincent Cheng SET_U16_LSB(idtcm->channel[3].output_mask, val); 11353a6ba7dcSVincent Cheng break; 11367ea5fda2SMin Li case TOD3_OUT_ALIGN_MASK_ADDR + 1: 11373a6ba7dcSVincent Cheng SET_U16_MSB(idtcm->channel[3].output_mask, val); 11383a6ba7dcSVincent Cheng break; 11393a6ba7dcSVincent Cheng default: 11407ea5fda2SMin Li err = -EFAULT; /* Bad address */; 11413a6ba7dcSVincent Cheng break; 11423a6ba7dcSVincent Cheng } 11433a6ba7dcSVincent Cheng 11443a6ba7dcSVincent Cheng return err; 11453a6ba7dcSVincent Cheng } 11463a6ba7dcSVincent Cheng 11477ea5fda2SMin Li static int set_tod_ptp_pll(struct idtcm *idtcm, u8 index, u8 pll) 11487ea5fda2SMin Li { 11497ea5fda2SMin Li if (index >= MAX_TOD) { 11501c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "ToD%d not supported", index); 11517ea5fda2SMin Li return -EINVAL; 11527ea5fda2SMin Li } 11537ea5fda2SMin Li 11547ea5fda2SMin Li if (pll >= MAX_PLL) { 11551c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Pll%d not supported", pll); 11567ea5fda2SMin Li return -EINVAL; 11577ea5fda2SMin Li } 11587ea5fda2SMin Li 11597ea5fda2SMin Li idtcm->channel[index].pll = pll; 11607ea5fda2SMin Li 11617ea5fda2SMin Li return 0; 11627ea5fda2SMin Li } 11637ea5fda2SMin Li 11643a6ba7dcSVincent Cheng static int check_and_set_masks(struct idtcm *idtcm, 11653a6ba7dcSVincent Cheng u16 regaddr, 11663a6ba7dcSVincent Cheng u8 val) 11673a6ba7dcSVincent Cheng { 11683a6ba7dcSVincent Cheng int err = 0; 11693a6ba7dcSVincent Cheng 11707ea5fda2SMin Li switch (regaddr) { 11717ea5fda2SMin Li case TOD_MASK_ADDR: 11727ea5fda2SMin Li if ((val & 0xf0) || !(val & 0x0f)) { 11731c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Invalid TOD mask 0x%02x", val); 11747ea5fda2SMin Li err = -EINVAL; 11757ea5fda2SMin Li } else { 11767ea5fda2SMin Li idtcm->tod_mask = val; 11777ea5fda2SMin Li } 11787ea5fda2SMin Li break; 11797ea5fda2SMin Li case TOD0_PTP_PLL_ADDR: 11807ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 0, val); 11817ea5fda2SMin Li break; 11827ea5fda2SMin Li case TOD1_PTP_PLL_ADDR: 11837ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 1, val); 11847ea5fda2SMin Li break; 11857ea5fda2SMin Li case TOD2_PTP_PLL_ADDR: 11867ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 2, val); 11877ea5fda2SMin Li break; 11887ea5fda2SMin Li case TOD3_PTP_PLL_ADDR: 11897ea5fda2SMin Li err = set_tod_ptp_pll(idtcm, 3, val); 11907ea5fda2SMin Li break; 11917ea5fda2SMin Li default: 11927ea5fda2SMin Li err = set_pll_output_mask(idtcm, regaddr, val); 11937ea5fda2SMin Li break; 11943a6ba7dcSVincent Cheng } 11953a6ba7dcSVincent Cheng 11963a6ba7dcSVincent Cheng return err; 11973a6ba7dcSVincent Cheng } 11983a6ba7dcSVincent Cheng 11997ea5fda2SMin Li static void display_pll_and_masks(struct idtcm *idtcm) 12003a6ba7dcSVincent Cheng { 12013a6ba7dcSVincent Cheng u8 i; 12023a6ba7dcSVincent Cheng u8 mask; 12033a6ba7dcSVincent Cheng 12041c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "tod_mask = 0x%02x", idtcm->tod_mask); 12053a6ba7dcSVincent Cheng 12067ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 12073a6ba7dcSVincent Cheng mask = 1 << i; 12083a6ba7dcSVincent Cheng 12097ea5fda2SMin Li if (mask & idtcm->tod_mask) 12103a6ba7dcSVincent Cheng dev_dbg(&idtcm->client->dev, 12111c49d3e9SVincent Cheng "TOD%d pll = %d output_mask = 0x%04x", 12127ea5fda2SMin Li i, idtcm->channel[i].pll, 12137ea5fda2SMin Li idtcm->channel[i].output_mask); 12143a6ba7dcSVincent Cheng } 12153a6ba7dcSVincent Cheng } 12163a6ba7dcSVincent Cheng 12173a6ba7dcSVincent Cheng static int idtcm_load_firmware(struct idtcm *idtcm, 12183a6ba7dcSVincent Cheng struct device *dev) 12193a6ba7dcSVincent Cheng { 12207ea5fda2SMin Li char fname[128] = FW_FILENAME; 12213a6ba7dcSVincent Cheng const struct firmware *fw; 12223a6ba7dcSVincent Cheng struct idtcm_fwrc *rec; 12233a6ba7dcSVincent Cheng u32 regaddr; 12243a6ba7dcSVincent Cheng int err; 12253a6ba7dcSVincent Cheng s32 len; 12263a6ba7dcSVincent Cheng u8 val; 12273a6ba7dcSVincent Cheng u8 loaddr; 12283a6ba7dcSVincent Cheng 12297ea5fda2SMin Li if (firmware) /* module parameter */ 12307ea5fda2SMin Li snprintf(fname, sizeof(fname), "%s", firmware); 12313a6ba7dcSVincent Cheng 12321c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "requesting firmware '%s'", fname); 12333a6ba7dcSVincent Cheng 12347ea5fda2SMin Li err = request_firmware(&fw, fname, dev); 12357ea5fda2SMin Li if (err) { 12367ea5fda2SMin Li dev_err(&idtcm->client->dev, 12371c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 12383a6ba7dcSVincent Cheng return err; 12397ea5fda2SMin Li } 12403a6ba7dcSVincent Cheng 12411c49d3e9SVincent Cheng dev_dbg(&idtcm->client->dev, "firmware size %zu bytes", fw->size); 12423a6ba7dcSVincent Cheng 12433a6ba7dcSVincent Cheng rec = (struct idtcm_fwrc *) fw->data; 12443a6ba7dcSVincent Cheng 1245251f4fe2SMin Li if (contains_full_configuration(fw)) 12463a6ba7dcSVincent Cheng idtcm_state_machine_reset(idtcm); 12473a6ba7dcSVincent Cheng 12483a6ba7dcSVincent Cheng for (len = fw->size; len > 0; len -= sizeof(*rec)) { 12493a6ba7dcSVincent Cheng if (rec->reserved) { 12503a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 12511c49d3e9SVincent Cheng "bad firmware, reserved field non-zero"); 12523a6ba7dcSVincent Cheng err = -EINVAL; 12533a6ba7dcSVincent Cheng } else { 12543a6ba7dcSVincent Cheng regaddr = rec->hiaddr << 8; 12553a6ba7dcSVincent Cheng regaddr |= rec->loaddr; 12563a6ba7dcSVincent Cheng 12573a6ba7dcSVincent Cheng val = rec->value; 12583a6ba7dcSVincent Cheng loaddr = rec->loaddr; 12593a6ba7dcSVincent Cheng 12603a6ba7dcSVincent Cheng rec++; 12613a6ba7dcSVincent Cheng 12623a6ba7dcSVincent Cheng err = check_and_set_masks(idtcm, regaddr, val); 12633a6ba7dcSVincent Cheng } 12643a6ba7dcSVincent Cheng 12657ea5fda2SMin Li if (err != -EINVAL) { 12667ea5fda2SMin Li err = 0; 12677ea5fda2SMin Li 12683a6ba7dcSVincent Cheng /* Top (status registers) and bottom are read-only */ 12693a6ba7dcSVincent Cheng if ((regaddr < GPIO_USER_CONTROL) 12703a6ba7dcSVincent Cheng || (regaddr >= SCRATCH)) 12713a6ba7dcSVincent Cheng continue; 12723a6ba7dcSVincent Cheng 12733a6ba7dcSVincent Cheng /* Page size 128, last 4 bytes of page skipped */ 12743a6ba7dcSVincent Cheng if (((loaddr > 0x7b) && (loaddr <= 0x7f)) 12753e14462fSYang Yingliang || loaddr > 0xfb) 12763a6ba7dcSVincent Cheng continue; 12773a6ba7dcSVincent Cheng 12783a6ba7dcSVincent Cheng err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val)); 12793a6ba7dcSVincent Cheng } 12803a6ba7dcSVincent Cheng 12813a6ba7dcSVincent Cheng if (err) 12823a6ba7dcSVincent Cheng goto out; 12833a6ba7dcSVincent Cheng } 12843a6ba7dcSVincent Cheng 12857ea5fda2SMin Li display_pll_and_masks(idtcm); 12863a6ba7dcSVincent Cheng 12873a6ba7dcSVincent Cheng out: 12883a6ba7dcSVincent Cheng release_firmware(fw); 12893a6ba7dcSVincent Cheng return err; 12903a6ba7dcSVincent Cheng } 12913a6ba7dcSVincent Cheng 12927ea5fda2SMin Li static int idtcm_output_enable(struct idtcm_channel *channel, 12937ea5fda2SMin Li bool enable, unsigned int outn) 12943a6ba7dcSVincent Cheng { 12953a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 12967260d1c8SMin Li int base; 12973a6ba7dcSVincent Cheng int err; 12987ea5fda2SMin Li u8 val; 12993a6ba7dcSVincent Cheng 13007260d1c8SMin Li base = get_output_base_addr(outn); 13017260d1c8SMin Li 13027260d1c8SMin Li if (!(base > 0)) { 13037260d1c8SMin Li dev_err(&idtcm->client->dev, 13047260d1c8SMin Li "%s - Unsupported out%d", __func__, outn); 13057260d1c8SMin Li return base; 13067260d1c8SMin Li } 13077260d1c8SMin Li 13087260d1c8SMin Li err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 13093a6ba7dcSVincent Cheng if (err) 13103a6ba7dcSVincent Cheng return err; 13113a6ba7dcSVincent Cheng 13123a6ba7dcSVincent Cheng if (enable) 13133a6ba7dcSVincent Cheng val |= SQUELCH_DISABLE; 13143a6ba7dcSVincent Cheng else 13153a6ba7dcSVincent Cheng val &= ~SQUELCH_DISABLE; 13163a6ba7dcSVincent Cheng 13177260d1c8SMin Li return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val)); 13187ea5fda2SMin Li } 13197ea5fda2SMin Li 13207ea5fda2SMin Li static int idtcm_output_mask_enable(struct idtcm_channel *channel, 13217ea5fda2SMin Li bool enable) 13227ea5fda2SMin Li { 13237ea5fda2SMin Li u16 mask; 13247ea5fda2SMin Li int err; 13257ea5fda2SMin Li u8 outn; 13267ea5fda2SMin Li 13277ea5fda2SMin Li mask = channel->output_mask; 13287ea5fda2SMin Li outn = 0; 13297ea5fda2SMin Li 13307ea5fda2SMin Li while (mask) { 13317ea5fda2SMin Li if (mask & 0x1) { 13327ea5fda2SMin Li err = idtcm_output_enable(channel, enable, outn); 13333a6ba7dcSVincent Cheng if (err) 13343a6ba7dcSVincent Cheng return err; 13357ea5fda2SMin Li } 13367ea5fda2SMin Li 13377ea5fda2SMin Li mask >>= 0x1; 13387ea5fda2SMin Li outn++; 13397ea5fda2SMin Li } 13403a6ba7dcSVincent Cheng 13413a6ba7dcSVincent Cheng return 0; 13423a6ba7dcSVincent Cheng } 13433a6ba7dcSVincent Cheng 13447ea5fda2SMin Li static int idtcm_perout_enable(struct idtcm_channel *channel, 13457ea5fda2SMin Li bool enable, 13467ea5fda2SMin Li struct ptp_perout_request *perout) 13477ea5fda2SMin Li { 1348e8b4d8b5SVincent Cheng struct idtcm *idtcm = channel->idtcm; 13497ea5fda2SMin Li unsigned int flags = perout->flags; 1350e8b4d8b5SVincent Cheng struct timespec64 ts = {0, 0}; 1351e8b4d8b5SVincent Cheng int err; 13527ea5fda2SMin Li 13537ea5fda2SMin Li if (flags == PEROUT_ENABLE_OUTPUT_MASK) 1354e8b4d8b5SVincent Cheng err = idtcm_output_mask_enable(channel, enable); 1355e8b4d8b5SVincent Cheng else 1356e8b4d8b5SVincent Cheng err = idtcm_output_enable(channel, enable, perout->index); 13577ea5fda2SMin Li 1358e8b4d8b5SVincent Cheng if (err) { 1359e8b4d8b5SVincent Cheng dev_err(&idtcm->client->dev, "Unable to set output enable"); 1360e8b4d8b5SVincent Cheng return err; 1361e8b4d8b5SVincent Cheng } 1362e8b4d8b5SVincent Cheng 1363e8b4d8b5SVincent Cheng /* Align output to internal 1 PPS */ 1364e8b4d8b5SVincent Cheng return _idtcm_settime(channel, &ts, SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS); 13657ea5fda2SMin Li } 13667ea5fda2SMin Li 13677260d1c8SMin Li static int idtcm_get_pll_mode(struct idtcm_channel *channel, 13687260d1c8SMin Li enum pll_mode *pll_mode) 13697260d1c8SMin Li { 13707260d1c8SMin Li struct idtcm *idtcm = channel->idtcm; 13717260d1c8SMin Li int err; 13727260d1c8SMin Li u8 dpll_mode; 13737260d1c8SMin Li 13747260d1c8SMin Li err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13757260d1c8SMin Li &dpll_mode, sizeof(dpll_mode)); 13767260d1c8SMin Li if (err) 13777260d1c8SMin Li return err; 13787260d1c8SMin Li 13797260d1c8SMin Li *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK; 13807260d1c8SMin Li 13817260d1c8SMin Li return 0; 13827260d1c8SMin Li } 13837260d1c8SMin Li 13843a6ba7dcSVincent Cheng static int idtcm_set_pll_mode(struct idtcm_channel *channel, 13853a6ba7dcSVincent Cheng enum pll_mode pll_mode) 13863a6ba7dcSVincent Cheng { 13873a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 13883a6ba7dcSVincent Cheng int err; 13893a6ba7dcSVincent Cheng u8 dpll_mode; 13903a6ba7dcSVincent Cheng 13913a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE, 13923a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 13933a6ba7dcSVincent Cheng if (err) 13943a6ba7dcSVincent Cheng return err; 13953a6ba7dcSVincent Cheng 13963a6ba7dcSVincent Cheng dpll_mode &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); 13973a6ba7dcSVincent Cheng 13983a6ba7dcSVincent Cheng dpll_mode |= (pll_mode << PLL_MODE_SHIFT); 13993a6ba7dcSVincent Cheng 14003a6ba7dcSVincent Cheng channel->pll_mode = pll_mode; 14013a6ba7dcSVincent Cheng 14023a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_n, DPLL_MODE, 14033a6ba7dcSVincent Cheng &dpll_mode, sizeof(dpll_mode)); 14043a6ba7dcSVincent Cheng if (err) 14053a6ba7dcSVincent Cheng return err; 14063a6ba7dcSVincent Cheng 14073a6ba7dcSVincent Cheng return 0; 14083a6ba7dcSVincent Cheng } 14093a6ba7dcSVincent Cheng 14103a6ba7dcSVincent Cheng /* PTP Hardware Clock interface */ 14113a6ba7dcSVincent Cheng 1412425d2b1cSVincent Cheng /** 1413425d2b1cSVincent Cheng * @brief Maximum absolute value for write phase offset in picoseconds 1414425d2b1cSVincent Cheng * 1415425d2b1cSVincent Cheng * Destination signed register is 32-bit register in resolution of 50ps 1416425d2b1cSVincent Cheng * 1417425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1418425d2b1cSVincent Cheng */ 1419425d2b1cSVincent Cheng static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns) 1420425d2b1cSVincent Cheng { 1421425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1422425d2b1cSVincent Cheng int err; 1423425d2b1cSVincent Cheng u8 i; 1424425d2b1cSVincent Cheng u8 buf[4] = {0}; 1425425d2b1cSVincent Cheng s32 phase_50ps; 1426425d2b1cSVincent Cheng s64 offset_ps; 1427425d2b1cSVincent Cheng 1428425d2b1cSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_PHASE) { 1429425d2b1cSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE); 1430425d2b1cSVincent Cheng if (err) 1431425d2b1cSVincent Cheng return err; 1432425d2b1cSVincent Cheng } 1433425d2b1cSVincent Cheng 1434425d2b1cSVincent Cheng offset_ps = (s64)delta_ns * 1000; 1435425d2b1cSVincent Cheng 1436425d2b1cSVincent Cheng /* 1437425d2b1cSVincent Cheng * Check for 32-bit signed max * 50: 1438425d2b1cSVincent Cheng * 1439425d2b1cSVincent Cheng * 0x7fffffff * 50 = 2147483647 * 50 = 107374182350 1440425d2b1cSVincent Cheng */ 1441425d2b1cSVincent Cheng if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS) 1442425d2b1cSVincent Cheng offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS; 1443425d2b1cSVincent Cheng else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS) 1444425d2b1cSVincent Cheng offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS; 1445425d2b1cSVincent Cheng 14467260d1c8SMin Li phase_50ps = div_s64(offset_ps, 50); 1447425d2b1cSVincent Cheng 1448425d2b1cSVincent Cheng for (i = 0; i < 4; i++) { 1449425d2b1cSVincent Cheng buf[i] = phase_50ps & 0xff; 1450425d2b1cSVincent Cheng phase_50ps >>= 8; 1451425d2b1cSVincent Cheng } 1452425d2b1cSVincent Cheng 1453425d2b1cSVincent Cheng err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE, 1454425d2b1cSVincent Cheng buf, sizeof(buf)); 1455425d2b1cSVincent Cheng 1456425d2b1cSVincent Cheng return err; 1457425d2b1cSVincent Cheng } 1458425d2b1cSVincent Cheng 14597ea5fda2SMin Li static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm) 14603a6ba7dcSVincent Cheng { 14613a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 14623a6ba7dcSVincent Cheng u8 i; 14633a6ba7dcSVincent Cheng int err; 14643a6ba7dcSVincent Cheng u8 buf[6] = {0}; 14653a6ba7dcSVincent Cheng s64 fcw; 14663a6ba7dcSVincent Cheng 14673a6ba7dcSVincent Cheng if (channel->pll_mode != PLL_MODE_WRITE_FREQUENCY) { 14683a6ba7dcSVincent Cheng err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY); 14693a6ba7dcSVincent Cheng if (err) 14703a6ba7dcSVincent Cheng return err; 14713a6ba7dcSVincent Cheng } 14723a6ba7dcSVincent Cheng 14733a6ba7dcSVincent Cheng /* 14743a6ba7dcSVincent Cheng * Frequency Control Word unit is: 1.11 * 10^-10 ppm 14753a6ba7dcSVincent Cheng * 14763a6ba7dcSVincent Cheng * adjfreq: 14773a6ba7dcSVincent Cheng * ppb * 10^9 14783a6ba7dcSVincent Cheng * FCW = ---------- 14793a6ba7dcSVincent Cheng * 111 14803a6ba7dcSVincent Cheng * 14813a6ba7dcSVincent Cheng * adjfine: 14823a6ba7dcSVincent Cheng * ppm_16 * 5^12 14833a6ba7dcSVincent Cheng * FCW = ------------- 14843a6ba7dcSVincent Cheng * 111 * 2^4 14853a6ba7dcSVincent Cheng */ 14863a6ba7dcSVincent Cheng 14873a6ba7dcSVincent Cheng /* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */ 14887ea5fda2SMin Li fcw = scaled_ppm * 244140625ULL; 14893a6ba7dcSVincent Cheng 14907260d1c8SMin Li fcw = div_s64(fcw, 1776); 14913a6ba7dcSVincent Cheng 14923a6ba7dcSVincent Cheng for (i = 0; i < 6; i++) { 14933a6ba7dcSVincent Cheng buf[i] = fcw & 0xff; 14943a6ba7dcSVincent Cheng fcw >>= 8; 14953a6ba7dcSVincent Cheng } 14963a6ba7dcSVincent Cheng 14973a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->dpll_freq, DPLL_WR_FREQ, 14983a6ba7dcSVincent Cheng buf, sizeof(buf)); 14993a6ba7dcSVincent Cheng 15003a6ba7dcSVincent Cheng return err; 15013a6ba7dcSVincent Cheng } 15023a6ba7dcSVincent Cheng 15033a6ba7dcSVincent Cheng static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 15043a6ba7dcSVincent Cheng { 1505*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15063a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15073a6ba7dcSVincent Cheng int err; 15083a6ba7dcSVincent Cheng 15093a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 15103a6ba7dcSVincent Cheng 15113a6ba7dcSVincent Cheng err = _idtcm_gettime(channel, ts); 15127ea5fda2SMin Li if (err) 15131c49d3e9SVincent Cheng dev_err(&idtcm->client->dev, "Failed at line %d in %s!", 15141c49d3e9SVincent Cheng __LINE__, __func__); 15157ea5fda2SMin Li 15163a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 15173a6ba7dcSVincent Cheng 15183a6ba7dcSVincent Cheng return err; 15193a6ba7dcSVincent Cheng } 15203a6ba7dcSVincent Cheng 1521da948233SMin Li static int idtcm_settime_deprecated(struct ptp_clock_info *ptp, 15223a6ba7dcSVincent Cheng const struct timespec64 *ts) 15233a6ba7dcSVincent Cheng { 1524*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15253a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15263a6ba7dcSVincent Cheng int err; 15273a6ba7dcSVincent Cheng 15283a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 15293a6ba7dcSVincent Cheng 1530da948233SMin Li err = _idtcm_settime_deprecated(channel, ts); 15317ea5fda2SMin Li if (err) 15327ea5fda2SMin Li dev_err(&idtcm->client->dev, 15331c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15347ea5fda2SMin Li 15357ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 15367ea5fda2SMin Li 15377ea5fda2SMin Li return err; 15387ea5fda2SMin Li } 15397ea5fda2SMin Li 1540da948233SMin Li static int idtcm_settime(struct ptp_clock_info *ptp, 15417ea5fda2SMin Li const struct timespec64 *ts) 15427ea5fda2SMin Li { 1543*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15447ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 15457ea5fda2SMin Li int err; 15467ea5fda2SMin Li 15477ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 15487ea5fda2SMin Li 1549da948233SMin Li err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 1550da948233SMin Li if (err) 1551da948233SMin Li dev_err(&idtcm->client->dev, 15521c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 1553da948233SMin Li 1554da948233SMin Li mutex_unlock(&idtcm->reg_lock); 1555da948233SMin Li 1556da948233SMin Li return err; 1557da948233SMin Li } 1558da948233SMin Li 1559da948233SMin Li static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta) 1560da948233SMin Li { 1561*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 1562da948233SMin Li struct idtcm *idtcm = channel->idtcm; 1563da948233SMin Li int err; 1564da948233SMin Li 1565da948233SMin Li mutex_lock(&idtcm->reg_lock); 1566da948233SMin Li 1567da948233SMin Li err = _idtcm_adjtime_deprecated(channel, delta); 15687ea5fda2SMin Li if (err) 15697ea5fda2SMin Li dev_err(&idtcm->client->dev, 15701c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15717ea5fda2SMin Li 15723a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 15733a6ba7dcSVincent Cheng 15743a6ba7dcSVincent Cheng return err; 15753a6ba7dcSVincent Cheng } 15763a6ba7dcSVincent Cheng 15773a6ba7dcSVincent Cheng static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta) 15783a6ba7dcSVincent Cheng { 1579*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 15803a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 15817ea5fda2SMin Li struct timespec64 ts; 15827ea5fda2SMin Li enum scsr_tod_write_type_sel type; 15837ea5fda2SMin Li int err; 15847ea5fda2SMin Li 1585da948233SMin Li if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) { 15867ea5fda2SMin Li err = idtcm_do_phase_pull_in(channel, delta, 0); 15877ea5fda2SMin Li if (err) 15887ea5fda2SMin Li dev_err(&idtcm->client->dev, 15891c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 15907ea5fda2SMin Li return err; 15917ea5fda2SMin Li } 15927ea5fda2SMin Li 15937ea5fda2SMin Li if (delta >= 0) { 15947ea5fda2SMin Li ts = ns_to_timespec64(delta); 15957ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_PLUS; 15967ea5fda2SMin Li } else { 15977ea5fda2SMin Li ts = ns_to_timespec64(-delta); 15987ea5fda2SMin Li type = SCSR_TOD_WR_TYPE_SEL_DELTA_MINUS; 15997ea5fda2SMin Li } 16007ea5fda2SMin Li 16017ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 16027ea5fda2SMin Li 1603da948233SMin Li err = _idtcm_settime(channel, &ts, type); 16047ea5fda2SMin Li if (err) 16057ea5fda2SMin Li dev_err(&idtcm->client->dev, 16061c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16077ea5fda2SMin Li 16083a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 16093a6ba7dcSVincent Cheng 16103a6ba7dcSVincent Cheng return err; 16113a6ba7dcSVincent Cheng } 16123a6ba7dcSVincent Cheng 1613425d2b1cSVincent Cheng static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta) 1614425d2b1cSVincent Cheng { 1615*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 1616425d2b1cSVincent Cheng struct idtcm *idtcm = channel->idtcm; 1617425d2b1cSVincent Cheng int err; 1618425d2b1cSVincent Cheng 1619425d2b1cSVincent Cheng mutex_lock(&idtcm->reg_lock); 1620425d2b1cSVincent Cheng 1621425d2b1cSVincent Cheng err = _idtcm_adjphase(channel, delta); 16227ea5fda2SMin Li if (err) 16237ea5fda2SMin Li dev_err(&idtcm->client->dev, 16241c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16257ea5fda2SMin Li 16267ea5fda2SMin Li mutex_unlock(&idtcm->reg_lock); 16277ea5fda2SMin Li 16287ea5fda2SMin Li return err; 16297ea5fda2SMin Li } 16307ea5fda2SMin Li 16317ea5fda2SMin Li static int idtcm_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 16327ea5fda2SMin Li { 1633*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 16347ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 16357ea5fda2SMin Li int err; 16367ea5fda2SMin Li 16377ea5fda2SMin Li mutex_lock(&idtcm->reg_lock); 16387ea5fda2SMin Li 16397ea5fda2SMin Li err = _idtcm_adjfine(channel, scaled_ppm); 16407ea5fda2SMin Li if (err) 16417ea5fda2SMin Li dev_err(&idtcm->client->dev, 16421c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16437ea5fda2SMin Li 1644425d2b1cSVincent Cheng mutex_unlock(&idtcm->reg_lock); 1645425d2b1cSVincent Cheng 1646425d2b1cSVincent Cheng return err; 1647425d2b1cSVincent Cheng } 1648425d2b1cSVincent Cheng 16493a6ba7dcSVincent Cheng static int idtcm_enable(struct ptp_clock_info *ptp, 16503a6ba7dcSVincent Cheng struct ptp_clock_request *rq, int on) 16513a6ba7dcSVincent Cheng { 16527ea5fda2SMin Li int err; 1653*fcfd3757SVincent Cheng struct idtcm_channel *channel = container_of(ptp, struct idtcm_channel, caps); 16543a6ba7dcSVincent Cheng 16553a6ba7dcSVincent Cheng switch (rq->type) { 16563a6ba7dcSVincent Cheng case PTP_CLK_REQ_PEROUT: 16577ea5fda2SMin Li if (!on) { 16587ea5fda2SMin Li err = idtcm_perout_enable(channel, false, &rq->perout); 16597ea5fda2SMin Li if (err) 16607ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16611c49d3e9SVincent Cheng "Failed at line %d in %s!", 16621c49d3e9SVincent Cheng __LINE__, __func__); 16637ea5fda2SMin Li return err; 16647ea5fda2SMin Li } 16653a6ba7dcSVincent Cheng 16663a6ba7dcSVincent Cheng /* Only accept a 1-PPS aligned to the second. */ 16673a6ba7dcSVincent Cheng if (rq->perout.start.nsec || rq->perout.period.sec != 1 || 16683a6ba7dcSVincent Cheng rq->perout.period.nsec) 16693a6ba7dcSVincent Cheng return -ERANGE; 16703a6ba7dcSVincent Cheng 16717ea5fda2SMin Li err = idtcm_perout_enable(channel, true, &rq->perout); 16727ea5fda2SMin Li if (err) 16737ea5fda2SMin Li dev_err(&channel->idtcm->client->dev, 16741c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 16757ea5fda2SMin Li return err; 16763a6ba7dcSVincent Cheng default: 16773a6ba7dcSVincent Cheng break; 16783a6ba7dcSVincent Cheng } 16793a6ba7dcSVincent Cheng 16803a6ba7dcSVincent Cheng return -EOPNOTSUPP; 16813a6ba7dcSVincent Cheng } 16823a6ba7dcSVincent Cheng 16837ea5fda2SMin Li static int _enable_pll_tod_sync(struct idtcm *idtcm, 16847ea5fda2SMin Li u8 pll, 16857ea5fda2SMin Li u8 sync_src, 16867ea5fda2SMin Li u8 qn, 16877ea5fda2SMin Li u8 qn_plus_1) 16887ea5fda2SMin Li { 16897ea5fda2SMin Li int err; 16907ea5fda2SMin Li u8 val; 16917ea5fda2SMin Li u16 dpll; 16927ea5fda2SMin Li u16 out0 = 0, out1 = 0; 16937ea5fda2SMin Li 16947ea5fda2SMin Li if ((qn == 0) && (qn_plus_1 == 0)) 16957ea5fda2SMin Li return 0; 16967ea5fda2SMin Li 16977ea5fda2SMin Li switch (pll) { 16987ea5fda2SMin Li case 0: 16997ea5fda2SMin Li dpll = DPLL_0; 17007ea5fda2SMin Li if (qn) 17017ea5fda2SMin Li out0 = OUTPUT_0; 17027ea5fda2SMin Li if (qn_plus_1) 17037ea5fda2SMin Li out1 = OUTPUT_1; 17047ea5fda2SMin Li break; 17057ea5fda2SMin Li case 1: 17067ea5fda2SMin Li dpll = DPLL_1; 17077ea5fda2SMin Li if (qn) 17087ea5fda2SMin Li out0 = OUTPUT_2; 17097ea5fda2SMin Li if (qn_plus_1) 17107ea5fda2SMin Li out1 = OUTPUT_3; 17117ea5fda2SMin Li break; 17127ea5fda2SMin Li case 2: 17137ea5fda2SMin Li dpll = DPLL_2; 17147ea5fda2SMin Li if (qn) 17157ea5fda2SMin Li out0 = OUTPUT_4; 17167ea5fda2SMin Li if (qn_plus_1) 17177ea5fda2SMin Li out1 = OUTPUT_5; 17187ea5fda2SMin Li break; 17197ea5fda2SMin Li case 3: 17207ea5fda2SMin Li dpll = DPLL_3; 17217ea5fda2SMin Li if (qn) 17227ea5fda2SMin Li out0 = OUTPUT_6; 17237ea5fda2SMin Li if (qn_plus_1) 17247ea5fda2SMin Li out1 = OUTPUT_7; 17257ea5fda2SMin Li break; 17267ea5fda2SMin Li case 4: 17277ea5fda2SMin Li dpll = DPLL_4; 17287ea5fda2SMin Li if (qn) 17297ea5fda2SMin Li out0 = OUTPUT_8; 17307ea5fda2SMin Li break; 17317ea5fda2SMin Li case 5: 17327ea5fda2SMin Li dpll = DPLL_5; 17337ea5fda2SMin Li if (qn) 17347ea5fda2SMin Li out0 = OUTPUT_9; 17357ea5fda2SMin Li if (qn_plus_1) 17367ea5fda2SMin Li out1 = OUTPUT_8; 17377ea5fda2SMin Li break; 17387ea5fda2SMin Li case 6: 17397ea5fda2SMin Li dpll = DPLL_6; 17407ea5fda2SMin Li if (qn) 17417ea5fda2SMin Li out0 = OUTPUT_10; 17427ea5fda2SMin Li if (qn_plus_1) 17437ea5fda2SMin Li out1 = OUTPUT_11; 17447ea5fda2SMin Li break; 17457ea5fda2SMin Li case 7: 17467ea5fda2SMin Li dpll = DPLL_7; 17477ea5fda2SMin Li if (qn) 17487ea5fda2SMin Li out0 = OUTPUT_11; 17497ea5fda2SMin Li break; 17507ea5fda2SMin Li default: 17517ea5fda2SMin Li return -EINVAL; 17527ea5fda2SMin Li } 17537ea5fda2SMin Li 17547ea5fda2SMin Li /* 17557ea5fda2SMin Li * Enable OUTPUT OUT_SYNC. 17567ea5fda2SMin Li */ 17577ea5fda2SMin Li if (out0) { 17587ea5fda2SMin Li err = idtcm_read(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17597ea5fda2SMin Li if (err) 17607ea5fda2SMin Li return err; 17617ea5fda2SMin Li 17627ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17637ea5fda2SMin Li 17647ea5fda2SMin Li err = idtcm_write(idtcm, out0, OUT_CTRL_1, &val, sizeof(val)); 17657ea5fda2SMin Li if (err) 17667ea5fda2SMin Li return err; 17677ea5fda2SMin Li } 17687ea5fda2SMin Li 17697ea5fda2SMin Li if (out1) { 17707ea5fda2SMin Li err = idtcm_read(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17717ea5fda2SMin Li if (err) 17727ea5fda2SMin Li return err; 17737ea5fda2SMin Li 17747ea5fda2SMin Li val &= ~OUT_SYNC_DISABLE; 17757ea5fda2SMin Li 17767ea5fda2SMin Li err = idtcm_write(idtcm, out1, OUT_CTRL_1, &val, sizeof(val)); 17777ea5fda2SMin Li if (err) 17787ea5fda2SMin Li return err; 17797ea5fda2SMin Li } 17807ea5fda2SMin Li 17817ea5fda2SMin Li /* enable dpll sync tod pps, must be set before dpll_mode */ 17827ea5fda2SMin Li err = idtcm_read(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 17837ea5fda2SMin Li if (err) 17847ea5fda2SMin Li return err; 17857ea5fda2SMin Li 17867ea5fda2SMin Li val &= ~(TOD_SYNC_SOURCE_MASK << TOD_SYNC_SOURCE_SHIFT); 17877ea5fda2SMin Li val |= (sync_src << TOD_SYNC_SOURCE_SHIFT); 17887ea5fda2SMin Li val |= TOD_SYNC_EN; 17897ea5fda2SMin Li 17907ea5fda2SMin Li return idtcm_write(idtcm, dpll, DPLL_TOD_SYNC_CFG, &val, sizeof(val)); 17917ea5fda2SMin Li } 17927ea5fda2SMin Li 17937ea5fda2SMin Li static int idtcm_enable_tod_sync(struct idtcm_channel *channel) 17947ea5fda2SMin Li { 17957ea5fda2SMin Li struct idtcm *idtcm = channel->idtcm; 17967ea5fda2SMin Li u8 pll; 17977ea5fda2SMin Li u8 sync_src; 17987ea5fda2SMin Li u8 qn; 17997ea5fda2SMin Li u8 qn_plus_1; 18007ea5fda2SMin Li u8 cfg; 18017ea5fda2SMin Li int err = 0; 18027ea5fda2SMin Li u16 output_mask = channel->output_mask; 18037ea5fda2SMin Li u8 out8_mux = 0; 18047ea5fda2SMin Li u8 out11_mux = 0; 18057ea5fda2SMin Li u8 temp; 18067ea5fda2SMin Li 18077ea5fda2SMin Li /* 18087ea5fda2SMin Li * set tod_out_sync_enable to 0. 18097ea5fda2SMin Li */ 18107ea5fda2SMin Li err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18117ea5fda2SMin Li if (err) 18127ea5fda2SMin Li return err; 18137ea5fda2SMin Li 18147ea5fda2SMin Li cfg &= ~TOD_OUT_SYNC_ENABLE; 18157ea5fda2SMin Li 18167ea5fda2SMin Li err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 18177ea5fda2SMin Li if (err) 18187ea5fda2SMin Li return err; 18197ea5fda2SMin Li 18207ea5fda2SMin Li switch (channel->tod_n) { 18217ea5fda2SMin Li case TOD_0: 18227ea5fda2SMin Li sync_src = 0; 18237ea5fda2SMin Li break; 18247ea5fda2SMin Li case TOD_1: 18257ea5fda2SMin Li sync_src = 1; 18267ea5fda2SMin Li break; 18277ea5fda2SMin Li case TOD_2: 18287ea5fda2SMin Li sync_src = 2; 18297ea5fda2SMin Li break; 18307ea5fda2SMin Li case TOD_3: 18317ea5fda2SMin Li sync_src = 3; 18327ea5fda2SMin Li break; 18337ea5fda2SMin Li default: 18347ea5fda2SMin Li return -EINVAL; 18357ea5fda2SMin Li } 18367ea5fda2SMin Li 1837*fcfd3757SVincent Cheng err = idtcm_read(idtcm, 0, HW_Q8_CTRL_SPARE, &temp, sizeof(temp)); 18387ea5fda2SMin Li if (err) 18397ea5fda2SMin Li return err; 18407ea5fda2SMin Li 18417ea5fda2SMin Li if ((temp & Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18427ea5fda2SMin Li Q9_TO_Q8_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18437ea5fda2SMin Li out8_mux = 1; 18447ea5fda2SMin Li 1845*fcfd3757SVincent Cheng err = idtcm_read(idtcm, 0, HW_Q11_CTRL_SPARE, &temp, sizeof(temp)); 18467ea5fda2SMin Li if (err) 18477ea5fda2SMin Li return err; 18487ea5fda2SMin Li 18497ea5fda2SMin Li if ((temp & Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) == 18507ea5fda2SMin Li Q10_TO_Q11_FANOUT_AND_CLOCK_SYNC_ENABLE_MASK) 18517ea5fda2SMin Li out11_mux = 1; 18527ea5fda2SMin Li 18537ea5fda2SMin Li for (pll = 0; pll < 8; pll++) { 18547ea5fda2SMin Li qn = 0; 18557ea5fda2SMin Li qn_plus_1 = 0; 18567ea5fda2SMin Li 18577ea5fda2SMin Li if (pll < 4) { 18587ea5fda2SMin Li /* First 4 pll has 2 outputs */ 18597ea5fda2SMin Li qn = output_mask & 0x1; 18607ea5fda2SMin Li output_mask = output_mask >> 1; 18617ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18627ea5fda2SMin Li output_mask = output_mask >> 1; 18637ea5fda2SMin Li } else if (pll == 4) { 18647ea5fda2SMin Li if (out8_mux == 0) { 18657ea5fda2SMin Li qn = output_mask & 0x1; 18667ea5fda2SMin Li output_mask = output_mask >> 1; 18677ea5fda2SMin Li } 18687ea5fda2SMin Li } else if (pll == 5) { 18697ea5fda2SMin Li if (out8_mux) { 18707ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18717ea5fda2SMin Li output_mask = output_mask >> 1; 18727ea5fda2SMin Li } 18737ea5fda2SMin Li qn = output_mask & 0x1; 18747ea5fda2SMin Li output_mask = output_mask >> 1; 18757ea5fda2SMin Li } else if (pll == 6) { 18767ea5fda2SMin Li qn = output_mask & 0x1; 18777ea5fda2SMin Li output_mask = output_mask >> 1; 18787ea5fda2SMin Li if (out11_mux) { 18797ea5fda2SMin Li qn_plus_1 = output_mask & 0x1; 18807ea5fda2SMin Li output_mask = output_mask >> 1; 18817ea5fda2SMin Li } 18827ea5fda2SMin Li } else if (pll == 7) { 18837ea5fda2SMin Li if (out11_mux == 0) { 18847ea5fda2SMin Li qn = output_mask & 0x1; 18857ea5fda2SMin Li output_mask = output_mask >> 1; 18867ea5fda2SMin Li } 18877ea5fda2SMin Li } 18887ea5fda2SMin Li 18897ea5fda2SMin Li if ((qn != 0) || (qn_plus_1 != 0)) 18907ea5fda2SMin Li err = _enable_pll_tod_sync(idtcm, pll, sync_src, qn, 18917ea5fda2SMin Li qn_plus_1); 18927ea5fda2SMin Li if (err) 18937ea5fda2SMin Li return err; 18947ea5fda2SMin Li } 18957ea5fda2SMin Li 18967ea5fda2SMin Li return err; 18977ea5fda2SMin Li } 18987ea5fda2SMin Li 18993a6ba7dcSVincent Cheng static int idtcm_enable_tod(struct idtcm_channel *channel) 19003a6ba7dcSVincent Cheng { 19013a6ba7dcSVincent Cheng struct idtcm *idtcm = channel->idtcm; 19023a6ba7dcSVincent Cheng struct timespec64 ts = {0, 0}; 19033a6ba7dcSVincent Cheng u8 cfg; 19043a6ba7dcSVincent Cheng int err; 19053a6ba7dcSVincent Cheng 19063a6ba7dcSVincent Cheng /* 19073a6ba7dcSVincent Cheng * Start the TOD clock ticking. 19083a6ba7dcSVincent Cheng */ 19093a6ba7dcSVincent Cheng err = idtcm_read(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19103a6ba7dcSVincent Cheng if (err) 19113a6ba7dcSVincent Cheng return err; 19123a6ba7dcSVincent Cheng 19133a6ba7dcSVincent Cheng cfg |= TOD_ENABLE; 19143a6ba7dcSVincent Cheng 19153a6ba7dcSVincent Cheng err = idtcm_write(idtcm, channel->tod_n, TOD_CFG, &cfg, sizeof(cfg)); 19163a6ba7dcSVincent Cheng if (err) 19173a6ba7dcSVincent Cheng return err; 19183a6ba7dcSVincent Cheng 1919da948233SMin Li if (idtcm->deprecated) 1920da948233SMin Li return _idtcm_settime_deprecated(channel, &ts); 1921da948233SMin Li else 1922da948233SMin Li return _idtcm_settime(channel, &ts, 1923da948233SMin Li SCSR_TOD_WR_TYPE_SEL_ABSOLUTE); 19243a6ba7dcSVincent Cheng } 19253a6ba7dcSVincent Cheng 1926da948233SMin Li static void idtcm_set_version_info(struct idtcm *idtcm) 19273a6ba7dcSVincent Cheng { 19283a6ba7dcSVincent Cheng u8 major; 19293a6ba7dcSVincent Cheng u8 minor; 19303a6ba7dcSVincent Cheng u8 hotfix; 19313a6ba7dcSVincent Cheng u16 product_id; 19323a6ba7dcSVincent Cheng u8 hw_rev_id; 19331ece2fbeSVincent Cheng u8 config_select; 19343a6ba7dcSVincent Cheng 19353a6ba7dcSVincent Cheng idtcm_read_major_release(idtcm, &major); 19363a6ba7dcSVincent Cheng idtcm_read_minor_release(idtcm, &minor); 19373a6ba7dcSVincent Cheng idtcm_read_hotfix_release(idtcm, &hotfix); 19383a6ba7dcSVincent Cheng 19393a6ba7dcSVincent Cheng idtcm_read_product_id(idtcm, &product_id); 19403a6ba7dcSVincent Cheng idtcm_read_hw_rev_id(idtcm, &hw_rev_id); 19413a6ba7dcSVincent Cheng 19421ece2fbeSVincent Cheng idtcm_read_otp_scsr_config_select(idtcm, &config_select); 19431ece2fbeSVincent Cheng 19447ea5fda2SMin Li snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u", 19457ea5fda2SMin Li major, minor, hotfix); 19467ea5fda2SMin Li 1947da948233SMin Li if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) 1948da948233SMin Li idtcm->deprecated = 0; 1949da948233SMin Li else 1950da948233SMin Li idtcm->deprecated = 1; 1951da948233SMin Li 19521c49d3e9SVincent Cheng dev_info(&idtcm->client->dev, 19531c49d3e9SVincent Cheng "%d.%d.%d, Id: 0x%04x HW Rev: %d OTP Config Select: %d", 19541c49d3e9SVincent Cheng major, minor, hotfix, 19551ece2fbeSVincent Cheng product_id, hw_rev_id, config_select); 19563a6ba7dcSVincent Cheng } 19573a6ba7dcSVincent Cheng 19586485f9aeSJulia Lawall static const struct ptp_clock_info idtcm_caps = { 19593a6ba7dcSVincent Cheng .owner = THIS_MODULE, 19603a6ba7dcSVincent Cheng .max_adj = 244000, 19617ea5fda2SMin Li .n_per_out = 12, 1962425d2b1cSVincent Cheng .adjphase = &idtcm_adjphase, 19637ea5fda2SMin Li .adjfine = &idtcm_adjfine, 19643a6ba7dcSVincent Cheng .adjtime = &idtcm_adjtime, 19653a6ba7dcSVincent Cheng .gettime64 = &idtcm_gettime, 19663a6ba7dcSVincent Cheng .settime64 = &idtcm_settime, 19673a6ba7dcSVincent Cheng .enable = &idtcm_enable, 19683a6ba7dcSVincent Cheng }; 19693a6ba7dcSVincent Cheng 1970da948233SMin Li static const struct ptp_clock_info idtcm_caps_deprecated = { 1971da948233SMin Li .owner = THIS_MODULE, 1972da948233SMin Li .max_adj = 244000, 1973da948233SMin Li .n_per_out = 12, 1974da948233SMin Li .adjphase = &idtcm_adjphase, 1975da948233SMin Li .adjfine = &idtcm_adjfine, 1976da948233SMin Li .adjtime = &idtcm_adjtime_deprecated, 1977da948233SMin Li .gettime64 = &idtcm_gettime, 1978da948233SMin Li .settime64 = &idtcm_settime_deprecated, 1979da948233SMin Li .enable = &idtcm_enable, 1980da948233SMin Li }; 1981da948233SMin Li 19827ea5fda2SMin Li static int configure_channel_pll(struct idtcm_channel *channel) 19833a6ba7dcSVincent Cheng { 19847ea5fda2SMin Li int err = 0; 19853a6ba7dcSVincent Cheng 19867ea5fda2SMin Li switch (channel->pll) { 19873a6ba7dcSVincent Cheng case 0: 19883a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_0; 19893a6ba7dcSVincent Cheng channel->dpll_n = DPLL_0; 19903a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_0; 19913a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_0; 19923a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_0; 19933a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_0; 19943a6ba7dcSVincent Cheng break; 19953a6ba7dcSVincent Cheng case 1: 19963a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_1; 19973a6ba7dcSVincent Cheng channel->dpll_n = DPLL_1; 19983a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_1; 19993a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_1; 20003a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_1; 20013a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_1; 20023a6ba7dcSVincent Cheng break; 20033a6ba7dcSVincent Cheng case 2: 20043a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_2; 20053a6ba7dcSVincent Cheng channel->dpll_n = DPLL_2; 20063a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_2; 20073a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_2; 20083a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_2; 20093a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_2; 20103a6ba7dcSVincent Cheng break; 20113a6ba7dcSVincent Cheng case 3: 20123a6ba7dcSVincent Cheng channel->dpll_freq = DPLL_FREQ_3; 20133a6ba7dcSVincent Cheng channel->dpll_n = DPLL_3; 20143a6ba7dcSVincent Cheng channel->hw_dpll_n = HW_DPLL_3; 20153a6ba7dcSVincent Cheng channel->dpll_phase = DPLL_PHASE_3; 20163a6ba7dcSVincent Cheng channel->dpll_ctrl_n = DPLL_CTRL_3; 20173a6ba7dcSVincent Cheng channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_3; 20183a6ba7dcSVincent Cheng break; 20197ea5fda2SMin Li case 4: 20207ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_4; 20217ea5fda2SMin Li channel->dpll_n = DPLL_4; 20227ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_4; 20237ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_4; 20247ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_4; 20257ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_4; 20267ea5fda2SMin Li break; 20277ea5fda2SMin Li case 5: 20287ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_5; 20297ea5fda2SMin Li channel->dpll_n = DPLL_5; 20307ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_5; 20317ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_5; 20327ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_5; 20337ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_5; 20347ea5fda2SMin Li break; 20357ea5fda2SMin Li case 6: 20367ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_6; 20377ea5fda2SMin Li channel->dpll_n = DPLL_6; 20387ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_6; 20397ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_6; 20407ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_6; 20417ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_6; 20427ea5fda2SMin Li break; 20437ea5fda2SMin Li case 7: 20447ea5fda2SMin Li channel->dpll_freq = DPLL_FREQ_7; 20457ea5fda2SMin Li channel->dpll_n = DPLL_7; 20467ea5fda2SMin Li channel->hw_dpll_n = HW_DPLL_7; 20477ea5fda2SMin Li channel->dpll_phase = DPLL_PHASE_7; 20487ea5fda2SMin Li channel->dpll_ctrl_n = DPLL_CTRL_7; 20497ea5fda2SMin Li channel->dpll_phase_pull_in = DPLL_PHASE_PULL_IN_7; 20507ea5fda2SMin Li break; 20517ea5fda2SMin Li default: 20527ea5fda2SMin Li err = -EINVAL; 20537ea5fda2SMin Li } 20547ea5fda2SMin Li 20557ea5fda2SMin Li return err; 20567ea5fda2SMin Li } 20577ea5fda2SMin Li 20587ea5fda2SMin Li static int idtcm_enable_channel(struct idtcm *idtcm, u32 index) 20597ea5fda2SMin Li { 20607ea5fda2SMin Li struct idtcm_channel *channel; 20617ea5fda2SMin Li int err; 20627ea5fda2SMin Li 20637ea5fda2SMin Li if (!(index < MAX_TOD)) 20647ea5fda2SMin Li return -EINVAL; 20657ea5fda2SMin Li 20667ea5fda2SMin Li channel = &idtcm->channel[index]; 20677ea5fda2SMin Li 20687ea5fda2SMin Li /* Set pll addresses */ 20697ea5fda2SMin Li err = configure_channel_pll(channel); 20707ea5fda2SMin Li if (err) 20717ea5fda2SMin Li return err; 20727ea5fda2SMin Li 20737ea5fda2SMin Li /* Set tod addresses */ 20747ea5fda2SMin Li switch (index) { 20757ea5fda2SMin Li case 0: 20767ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_0; 20777ea5fda2SMin Li channel->tod_write = TOD_WRITE_0; 20787ea5fda2SMin Li channel->tod_n = TOD_0; 20797ea5fda2SMin Li break; 20807ea5fda2SMin Li case 1: 20817ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_1; 20827ea5fda2SMin Li channel->tod_write = TOD_WRITE_1; 20837ea5fda2SMin Li channel->tod_n = TOD_1; 20847ea5fda2SMin Li break; 20857ea5fda2SMin Li case 2: 20867ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_2; 20877ea5fda2SMin Li channel->tod_write = TOD_WRITE_2; 20887ea5fda2SMin Li channel->tod_n = TOD_2; 20897ea5fda2SMin Li break; 20907ea5fda2SMin Li case 3: 20917ea5fda2SMin Li channel->tod_read_primary = TOD_READ_PRIMARY_3; 20927ea5fda2SMin Li channel->tod_write = TOD_WRITE_3; 20937ea5fda2SMin Li channel->tod_n = TOD_3; 20947ea5fda2SMin Li break; 20953a6ba7dcSVincent Cheng default: 20963a6ba7dcSVincent Cheng return -EINVAL; 20973a6ba7dcSVincent Cheng } 20983a6ba7dcSVincent Cheng 20993a6ba7dcSVincent Cheng channel->idtcm = idtcm; 21003a6ba7dcSVincent Cheng 2101da948233SMin Li if (idtcm->deprecated) 2102da948233SMin Li channel->caps = idtcm_caps_deprecated; 21037ea5fda2SMin Li else 21043a6ba7dcSVincent Cheng channel->caps = idtcm_caps; 21057ea5fda2SMin Li 21063a6ba7dcSVincent Cheng snprintf(channel->caps.name, sizeof(channel->caps.name), 21077ea5fda2SMin Li "IDT CM TOD%u", index); 21087ea5fda2SMin Li 2109da948233SMin Li if (!idtcm->deprecated) { 21107ea5fda2SMin Li err = idtcm_enable_tod_sync(channel); 21117ea5fda2SMin Li if (err) { 21127ea5fda2SMin Li dev_err(&idtcm->client->dev, 21131c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 21147ea5fda2SMin Li return err; 21157ea5fda2SMin Li } 21167ea5fda2SMin Li } 21173a6ba7dcSVincent Cheng 21187260d1c8SMin Li /* Sync pll mode with hardware */ 21197260d1c8SMin Li err = idtcm_get_pll_mode(channel, &channel->pll_mode); 21207ea5fda2SMin Li if (err) { 21217ea5fda2SMin Li dev_err(&idtcm->client->dev, 21221c49d3e9SVincent Cheng "Error: %s - Unable to read pll mode", __func__); 21233a6ba7dcSVincent Cheng return err; 21247ea5fda2SMin Li } 21253a6ba7dcSVincent Cheng 21263a6ba7dcSVincent Cheng err = idtcm_enable_tod(channel); 21277ea5fda2SMin Li if (err) { 21287ea5fda2SMin Li dev_err(&idtcm->client->dev, 21291c49d3e9SVincent Cheng "Failed at line %d in %s!", __LINE__, __func__); 21303a6ba7dcSVincent Cheng return err; 21317ea5fda2SMin Li } 21323a6ba7dcSVincent Cheng 21333a6ba7dcSVincent Cheng channel->ptp_clock = ptp_clock_register(&channel->caps, NULL); 21343a6ba7dcSVincent Cheng 21353a6ba7dcSVincent Cheng if (IS_ERR(channel->ptp_clock)) { 21363a6ba7dcSVincent Cheng err = PTR_ERR(channel->ptp_clock); 21373a6ba7dcSVincent Cheng channel->ptp_clock = NULL; 21383a6ba7dcSVincent Cheng return err; 21393a6ba7dcSVincent Cheng } 21403a6ba7dcSVincent Cheng 21413a6ba7dcSVincent Cheng if (!channel->ptp_clock) 21423a6ba7dcSVincent Cheng return -ENOTSUPP; 21433a6ba7dcSVincent Cheng 21441c49d3e9SVincent Cheng dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d", 21453a6ba7dcSVincent Cheng index, channel->ptp_clock->index); 21463a6ba7dcSVincent Cheng 21473a6ba7dcSVincent Cheng return 0; 21483a6ba7dcSVincent Cheng } 21493a6ba7dcSVincent Cheng 21503a6ba7dcSVincent Cheng static void ptp_clock_unregister_all(struct idtcm *idtcm) 21513a6ba7dcSVincent Cheng { 21523a6ba7dcSVincent Cheng u8 i; 21533a6ba7dcSVincent Cheng struct idtcm_channel *channel; 21543a6ba7dcSVincent Cheng 21557ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 21563a6ba7dcSVincent Cheng channel = &idtcm->channel[i]; 21573a6ba7dcSVincent Cheng 21583a6ba7dcSVincent Cheng if (channel->ptp_clock) 21593a6ba7dcSVincent Cheng ptp_clock_unregister(channel->ptp_clock); 21603a6ba7dcSVincent Cheng } 21613a6ba7dcSVincent Cheng } 21623a6ba7dcSVincent Cheng 21633a6ba7dcSVincent Cheng static void set_default_masks(struct idtcm *idtcm) 21643a6ba7dcSVincent Cheng { 21657ea5fda2SMin Li idtcm->tod_mask = DEFAULT_TOD_MASK; 21667ea5fda2SMin Li 21677ea5fda2SMin Li idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL; 21687ea5fda2SMin Li idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL; 21697ea5fda2SMin Li idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL; 21707ea5fda2SMin Li idtcm->channel[3].pll = DEFAULT_TOD3_PTP_PLL; 21713a6ba7dcSVincent Cheng 21723a6ba7dcSVincent Cheng idtcm->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0; 21733a6ba7dcSVincent Cheng idtcm->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1; 21743a6ba7dcSVincent Cheng idtcm->channel[2].output_mask = DEFAULT_OUTPUT_MASK_PLL2; 21753a6ba7dcSVincent Cheng idtcm->channel[3].output_mask = DEFAULT_OUTPUT_MASK_PLL3; 21763a6ba7dcSVincent Cheng } 21773a6ba7dcSVincent Cheng 21783a6ba7dcSVincent Cheng static int idtcm_probe(struct i2c_client *client, 21793a6ba7dcSVincent Cheng const struct i2c_device_id *id) 21803a6ba7dcSVincent Cheng { 21813a6ba7dcSVincent Cheng struct idtcm *idtcm; 21823a6ba7dcSVincent Cheng int err; 21833a6ba7dcSVincent Cheng u8 i; 21843a6ba7dcSVincent Cheng 21853a6ba7dcSVincent Cheng /* Unused for now */ 21863a6ba7dcSVincent Cheng (void)id; 21873a6ba7dcSVincent Cheng 21883a6ba7dcSVincent Cheng idtcm = devm_kzalloc(&client->dev, sizeof(struct idtcm), GFP_KERNEL); 21893a6ba7dcSVincent Cheng 21903a6ba7dcSVincent Cheng if (!idtcm) 21913a6ba7dcSVincent Cheng return -ENOMEM; 21923a6ba7dcSVincent Cheng 21933a6ba7dcSVincent Cheng idtcm->client = client; 21943a6ba7dcSVincent Cheng idtcm->page_offset = 0xff; 21953a6ba7dcSVincent Cheng idtcm->calculate_overhead_flag = 0; 21963a6ba7dcSVincent Cheng 21973a6ba7dcSVincent Cheng set_default_masks(idtcm); 21983a6ba7dcSVincent Cheng 21993a6ba7dcSVincent Cheng mutex_init(&idtcm->reg_lock); 22003a6ba7dcSVincent Cheng mutex_lock(&idtcm->reg_lock); 22013a6ba7dcSVincent Cheng 2202da948233SMin Li idtcm_set_version_info(idtcm); 22033a6ba7dcSVincent Cheng 22043a6ba7dcSVincent Cheng err = idtcm_load_firmware(idtcm, &client->dev); 22053a6ba7dcSVincent Cheng if (err) 2206*fcfd3757SVincent Cheng dev_warn(&idtcm->client->dev, "loading firmware failed with %d", err); 22073a6ba7dcSVincent Cheng 2208797d3186SVincent Cheng wait_for_chip_ready(idtcm); 2209251f4fe2SMin Li 22107ea5fda2SMin Li if (idtcm->tod_mask) { 22117ea5fda2SMin Li for (i = 0; i < MAX_TOD; i++) { 22127ea5fda2SMin Li if (idtcm->tod_mask & (1 << i)) { 22133a6ba7dcSVincent Cheng err = idtcm_enable_channel(idtcm, i); 22147ea5fda2SMin Li if (err) { 22157ea5fda2SMin Li dev_err(&idtcm->client->dev, 22161c49d3e9SVincent Cheng "idtcm_enable_channel %d failed!", i); 22173a6ba7dcSVincent Cheng break; 22183a6ba7dcSVincent Cheng } 22193a6ba7dcSVincent Cheng } 22207ea5fda2SMin Li } 22213a6ba7dcSVincent Cheng } else { 22223a6ba7dcSVincent Cheng dev_err(&idtcm->client->dev, 22231c49d3e9SVincent Cheng "no PLLs flagged as PHCs, nothing to do"); 22243a6ba7dcSVincent Cheng err = -ENODEV; 22253a6ba7dcSVincent Cheng } 22263a6ba7dcSVincent Cheng 22273a6ba7dcSVincent Cheng mutex_unlock(&idtcm->reg_lock); 22283a6ba7dcSVincent Cheng 22293a6ba7dcSVincent Cheng if (err) { 22303a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22313a6ba7dcSVincent Cheng return err; 22323a6ba7dcSVincent Cheng } 22333a6ba7dcSVincent Cheng 22343a6ba7dcSVincent Cheng i2c_set_clientdata(client, idtcm); 22353a6ba7dcSVincent Cheng 22363a6ba7dcSVincent Cheng return 0; 22373a6ba7dcSVincent Cheng } 22383a6ba7dcSVincent Cheng 22393a6ba7dcSVincent Cheng static int idtcm_remove(struct i2c_client *client) 22403a6ba7dcSVincent Cheng { 22413a6ba7dcSVincent Cheng struct idtcm *idtcm = i2c_get_clientdata(client); 22423a6ba7dcSVincent Cheng 22433a6ba7dcSVincent Cheng ptp_clock_unregister_all(idtcm); 22443a6ba7dcSVincent Cheng 22453a6ba7dcSVincent Cheng mutex_destroy(&idtcm->reg_lock); 22463a6ba7dcSVincent Cheng 22473a6ba7dcSVincent Cheng return 0; 22483a6ba7dcSVincent Cheng } 22493a6ba7dcSVincent Cheng 22503a6ba7dcSVincent Cheng #ifdef CONFIG_OF 22513a6ba7dcSVincent Cheng static const struct of_device_id idtcm_dt_id[] = { 22523a6ba7dcSVincent Cheng { .compatible = "idt,8a34000" }, 22533a6ba7dcSVincent Cheng { .compatible = "idt,8a34001" }, 22543a6ba7dcSVincent Cheng { .compatible = "idt,8a34002" }, 22553a6ba7dcSVincent Cheng { .compatible = "idt,8a34003" }, 22563a6ba7dcSVincent Cheng { .compatible = "idt,8a34004" }, 22573a6ba7dcSVincent Cheng { .compatible = "idt,8a34005" }, 22583a6ba7dcSVincent Cheng { .compatible = "idt,8a34006" }, 22593a6ba7dcSVincent Cheng { .compatible = "idt,8a34007" }, 22603a6ba7dcSVincent Cheng { .compatible = "idt,8a34008" }, 22613a6ba7dcSVincent Cheng { .compatible = "idt,8a34009" }, 22623a6ba7dcSVincent Cheng { .compatible = "idt,8a34010" }, 22633a6ba7dcSVincent Cheng { .compatible = "idt,8a34011" }, 22643a6ba7dcSVincent Cheng { .compatible = "idt,8a34012" }, 22653a6ba7dcSVincent Cheng { .compatible = "idt,8a34013" }, 22663a6ba7dcSVincent Cheng { .compatible = "idt,8a34014" }, 22673a6ba7dcSVincent Cheng { .compatible = "idt,8a34015" }, 22683a6ba7dcSVincent Cheng { .compatible = "idt,8a34016" }, 22693a6ba7dcSVincent Cheng { .compatible = "idt,8a34017" }, 22703a6ba7dcSVincent Cheng { .compatible = "idt,8a34018" }, 22713a6ba7dcSVincent Cheng { .compatible = "idt,8a34019" }, 22723a6ba7dcSVincent Cheng { .compatible = "idt,8a34040" }, 22733a6ba7dcSVincent Cheng { .compatible = "idt,8a34041" }, 22743a6ba7dcSVincent Cheng { .compatible = "idt,8a34042" }, 22753a6ba7dcSVincent Cheng { .compatible = "idt,8a34043" }, 22763a6ba7dcSVincent Cheng { .compatible = "idt,8a34044" }, 22773a6ba7dcSVincent Cheng { .compatible = "idt,8a34045" }, 22783a6ba7dcSVincent Cheng { .compatible = "idt,8a34046" }, 22793a6ba7dcSVincent Cheng { .compatible = "idt,8a34047" }, 22803a6ba7dcSVincent Cheng { .compatible = "idt,8a34048" }, 22813a6ba7dcSVincent Cheng { .compatible = "idt,8a34049" }, 22823a6ba7dcSVincent Cheng {}, 22833a6ba7dcSVincent Cheng }; 22843a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(of, idtcm_dt_id); 22853a6ba7dcSVincent Cheng #endif 22863a6ba7dcSVincent Cheng 22873a6ba7dcSVincent Cheng static const struct i2c_device_id idtcm_i2c_id[] = { 22883a6ba7dcSVincent Cheng { "8a34000" }, 22893a6ba7dcSVincent Cheng { "8a34001" }, 22903a6ba7dcSVincent Cheng { "8a34002" }, 22913a6ba7dcSVincent Cheng { "8a34003" }, 22923a6ba7dcSVincent Cheng { "8a34004" }, 22933a6ba7dcSVincent Cheng { "8a34005" }, 22943a6ba7dcSVincent Cheng { "8a34006" }, 22953a6ba7dcSVincent Cheng { "8a34007" }, 22963a6ba7dcSVincent Cheng { "8a34008" }, 22973a6ba7dcSVincent Cheng { "8a34009" }, 22983a6ba7dcSVincent Cheng { "8a34010" }, 22993a6ba7dcSVincent Cheng { "8a34011" }, 23003a6ba7dcSVincent Cheng { "8a34012" }, 23013a6ba7dcSVincent Cheng { "8a34013" }, 23023a6ba7dcSVincent Cheng { "8a34014" }, 23033a6ba7dcSVincent Cheng { "8a34015" }, 23043a6ba7dcSVincent Cheng { "8a34016" }, 23053a6ba7dcSVincent Cheng { "8a34017" }, 23063a6ba7dcSVincent Cheng { "8a34018" }, 23073a6ba7dcSVincent Cheng { "8a34019" }, 23083a6ba7dcSVincent Cheng { "8a34040" }, 23093a6ba7dcSVincent Cheng { "8a34041" }, 23103a6ba7dcSVincent Cheng { "8a34042" }, 23113a6ba7dcSVincent Cheng { "8a34043" }, 23123a6ba7dcSVincent Cheng { "8a34044" }, 23133a6ba7dcSVincent Cheng { "8a34045" }, 23143a6ba7dcSVincent Cheng { "8a34046" }, 23153a6ba7dcSVincent Cheng { "8a34047" }, 23163a6ba7dcSVincent Cheng { "8a34048" }, 23173a6ba7dcSVincent Cheng { "8a34049" }, 23183a6ba7dcSVincent Cheng {}, 23193a6ba7dcSVincent Cheng }; 23203a6ba7dcSVincent Cheng MODULE_DEVICE_TABLE(i2c, idtcm_i2c_id); 23213a6ba7dcSVincent Cheng 23223a6ba7dcSVincent Cheng static struct i2c_driver idtcm_driver = { 23233a6ba7dcSVincent Cheng .driver = { 23243a6ba7dcSVincent Cheng .of_match_table = of_match_ptr(idtcm_dt_id), 23253a6ba7dcSVincent Cheng .name = "idtcm", 23263a6ba7dcSVincent Cheng }, 23273a6ba7dcSVincent Cheng .probe = idtcm_probe, 23283a6ba7dcSVincent Cheng .remove = idtcm_remove, 23293a6ba7dcSVincent Cheng .id_table = idtcm_i2c_id, 23303a6ba7dcSVincent Cheng }; 23313a6ba7dcSVincent Cheng 23323a6ba7dcSVincent Cheng module_i2c_driver(idtcm_driver); 2333