aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ptp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ptp')
-rw-r--r--drivers/ptp/Kconfig14
-rw-r--r--drivers/ptp/Makefile1
-rw-r--r--drivers/ptp/idt8a340_reg.h1
-rw-r--r--drivers/ptp/ptp_clockmatrix.c377
-rw-r--r--drivers/ptp/ptp_clockmatrix.h24
-rw-r--r--drivers/ptp/ptp_idt82p33.c274
-rw-r--r--drivers/ptp/ptp_idt82p33.h3
-rw-r--r--drivers/ptp/ptp_ines.c19
-rw-r--r--drivers/ptp/ptp_ocp.c398
9 files changed, 874 insertions, 237 deletions
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 942f72d8151d..476d7c7fe70a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -151,4 +151,18 @@ config PTP_1588_CLOCK_VMW
To compile this driver as a module, choose M here: the module
will be called ptp_vmw.
+config PTP_1588_CLOCK_OCP
+ tristate "OpenCompute TimeCard as PTP clock"
+ depends on PTP_1588_CLOCK
+ depends on HAS_IOMEM && PCI
+ default n
+ help
+ This driver adds support for an OpenCompute time card.
+
+ The OpenCompute time card is an atomic clock along with
+ a GPS receiver that provides a Grandmaster clock source
+ for a PTP enabled network.
+
+ More information is available at http://www.timingcard.com/
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 7aff75f745dc..db5aef3bddc6 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -15,3 +15,4 @@ ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o
obj-$(CONFIG_PTP_1588_CLOCK_IDT82P33) += ptp_idt82p33.o
obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
+obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
diff --git a/drivers/ptp/idt8a340_reg.h b/drivers/ptp/idt8a340_reg.h
index b297c4aba2ba..a664dfe5fd2f 100644
--- a/drivers/ptp/idt8a340_reg.h
+++ b/drivers/ptp/idt8a340_reg.h
@@ -103,6 +103,7 @@
#define SM_RESET_CMD 0x5A
#define GENERAL_STATUS 0xc014
+#define BOOT_STATUS 0x0000
#define HW_REV_ID 0x000A
#define BOND_ID 0x000B
#define HW_CSR_ID 0x000C
diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index e020faff7da5..051511f5e1f2 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -33,14 +33,41 @@ module_param(firmware, charp, 0);
#define SETTIME_CORRECTION (0)
-static long set_write_phase_ready(struct ptp_clock_info *ptp)
+static int contains_full_configuration(const struct firmware *fw)
{
- struct idtcm_channel *channel =
- container_of(ptp, struct idtcm_channel, caps);
+ s32 full_count = FULL_FW_CFG_BYTES - FULL_FW_CFG_SKIPPED_BYTES;
+ struct idtcm_fwrc *rec = (struct idtcm_fwrc *)fw->data;
+ s32 count = 0;
+ u16 regaddr;
+ u8 loaddr;
+ s32 len;
+
+ /* If the firmware contains 'full configuration' SM_RESET can be used
+ * to ensure proper configuration.
+ *
+ * Full configuration is defined as the number of programmable
+ * bytes within the configuration range minus page offset addr range.
+ */
+ for (len = fw->size; len > 0; len -= sizeof(*rec)) {
+ regaddr = rec->hiaddr << 8;
+ regaddr |= rec->loaddr;
- channel->write_phase_ready = 1;
+ loaddr = rec->loaddr;
- return 0;
+ rec++;
+
+ /* Top (status registers) and bottom are read-only */
+ if (regaddr < GPIO_USER_CONTROL || regaddr >= SCRATCH)
+ continue;
+
+ /* Page size 128, last 4 bytes of page skipped */
+ if ((loaddr > 0x7b && loaddr <= 0x7f) || loaddr > 0xfb)
+ continue;
+
+ count++;
+ }
+
+ return (count >= full_count);
}
static int char_array_to_timespec(u8 *buf,
@@ -103,43 +130,26 @@ static int timespec_to_char_array(struct timespec64 const *ts,
return 0;
}
-static int idtcm_strverscmp(const char *ver1, const char *ver2)
+static int idtcm_strverscmp(const char *version1, const char *version2)
{
- u8 num1;
- u8 num2;
- int result = 0;
+ u8 ver1[3], ver2[3];
+ int i;
- /* loop through each level of the version string */
- while (result == 0) {
- /* extract leading version numbers */
- if (kstrtou8(ver1, 10, &num1) < 0)
- return -1;
+ if (sscanf(version1, "%hhu.%hhu.%hhu",
+ &ver1[0], &ver1[1], &ver1[2]) != 3)
+ return -1;
+ if (sscanf(version2, "%hhu.%hhu.%hhu",
+ &ver2[0], &ver2[1], &ver2[2]) != 3)
+ return -1;
- if (kstrtou8(ver2, 10, &num2) < 0)
+ for (i = 0; i < 3; i++) {
+ if (ver1[i] > ver2[i])
+ return 1;
+ if (ver1[i] < ver2[i])
return -1;
-
- /* if numbers differ, then set the result */
- if (num1 < num2)
- result = -1;
- else if (num1 > num2)
- result = 1;
- else {
- /* if numbers are the same, go to next level */
- ver1 = strchr(ver1, '.');
- ver2 = strchr(ver2, '.');
- if (!ver1 && !ver2)
- break;
- else if (!ver1)
- result = -1;
- else if (!ver2)
- result = 1;
- else {
- ver1++;
- ver2++;
- }
- }
}
- return result;
+
+ return 0;
}
static int idtcm_xfer_read(struct idtcm *idtcm,
@@ -278,6 +288,53 @@ static int idtcm_write(struct idtcm *idtcm,
return _idtcm_rdwr(idtcm, module + regaddr, buf, count, true);
}
+static int clear_boot_status(struct idtcm *idtcm)
+{
+ int err;
+ u8 buf[4] = {0};
+
+ err = idtcm_write(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
+
+ return err;
+}
+
+static int read_boot_status(struct idtcm *idtcm, u32 *status)
+{
+ int err;
+ u8 buf[4] = {0};
+
+ err = idtcm_read(idtcm, GENERAL_STATUS, BOOT_STATUS, buf, sizeof(buf));
+
+ *status = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ return err;
+}
+
+static int wait_for_boot_status_ready(struct idtcm *idtcm)
+{
+ u32 status = 0;
+ u8 i = 30; /* 30 * 100ms = 3s */
+ int err;
+
+ do {
+ err = read_boot_status(idtcm, &status);
+
+ if (err)
+ return err;
+
+ if (status == 0xA0)
+ return 0;
+
+ msleep(100);
+ i--;
+
+ } while (i);
+
+ dev_warn(&idtcm->client->dev, "%s timed out\n", __func__);
+
+ return -EBUSY;
+}
+
static int _idtcm_gettime(struct idtcm_channel *channel,
struct timespec64 *ts)
{
@@ -616,8 +673,9 @@ static int _idtcm_set_dpll_hw_tod(struct idtcm_channel *channel,
if (idtcm->calculate_overhead_flag) {
/* Assumption: I2C @ 400KHz */
- total_overhead_ns = ktime_to_ns(ktime_get_raw()
- - idtcm->start_time)
+ ktime_t diff = ktime_sub(ktime_get_raw(),
+ idtcm->start_time);
+ total_overhead_ns = ktime_to_ns(diff)
+ idtcm->tod_write_overhead_ns
+ SETTIME_CORRECTION;
@@ -687,7 +745,7 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
if (err)
return err;
- if (cmd == 0)
+ if ((cmd & TOD_WRITE_SELECTION_MASK) == 0)
break;
if (++count > 20) {
@@ -700,49 +758,74 @@ static int _idtcm_set_dpll_scsr_tod(struct idtcm_channel *channel,
return 0;
}
-static int _idtcm_settime(struct idtcm_channel *channel,
- struct timespec64 const *ts,
- enum hw_tod_write_trig_sel wr_trig)
+static int get_output_base_addr(u8 outn)
{
- struct idtcm *idtcm = channel->idtcm;
- int err;
- int i;
- u8 trig_sel;
-
- err = _idtcm_set_dpll_hw_tod(channel, ts, wr_trig);
+ int base;
- if (err)
- return err;
-
- /* Wait for the operation to complete. */
- for (i = 0; i < 10000; i++) {
- err = idtcm_read(idtcm, channel->hw_dpll_n,
- HW_DPLL_TOD_CTRL_1, &trig_sel,
- sizeof(trig_sel));
+ switch (outn) {
+ case 0:
+ base = OUTPUT_0;
+ break;
+ case 1:
+ base = OUTPUT_1;
+ break;
+ case 2:
+ base = OUTPUT_2;
+ break;
+ case 3:
+ base = OUTPUT_3;
+ break;
+ case 4:
+ base = OUTPUT_4;
+ break;
+ case 5:
+ base = OUTPUT_5;
+ break;
+ case 6:
+ base = OUTPUT_6;
+ break;
+ case 7:
+ base = OUTPUT_7;
+ break;
+ case 8:
+ base = OUTPUT_8;
+ break;
+ case 9:
+ base = OUTPUT_9;
+ break;
+ case 10:
+ base = OUTPUT_10;
+ break;
+ case 11:
+ base = OUTPUT_11;
+ break;
+ default:
+ base = -EINVAL;
+ }
- if (err)
- return err;
+ return base;
+}
- if (trig_sel == 0x4a)
- break;
+static int _idtcm_settime_deprecated(struct idtcm_channel *channel,
+ struct timespec64 const *ts)
+{
+ struct idtcm *idtcm = channel->idtcm;
+ int err;
- err = 1;
- }
+ err = _idtcm_set_dpll_hw_tod(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
if (err) {
dev_err(&idtcm->client->dev,
- "Failed at line %d in func %s!\n",
- __LINE__,
- __func__);
+ "%s: Set HW ToD failed\n", __func__);
return err;
}
return idtcm_sync_pps_output(channel);
}
-static int _idtcm_settime_v487(struct idtcm_channel *channel,
- struct timespec64 const *ts,
- enum scsr_tod_write_type_sel wr_type)
+static int _idtcm_settime(struct idtcm_channel *channel,
+ struct timespec64 const *ts,
+ enum scsr_tod_write_type_sel wr_type)
{
return _idtcm_set_dpll_scsr_tod(channel, ts,
SCSR_TOD_WR_TRIG_SEL_IMMEDIATE,
@@ -847,6 +930,7 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)
ktime_t start;
ktime_t stop;
+ ktime_t diff;
char buf[TOD_BYTE_COUNT] = {0};
@@ -866,7 +950,9 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)
stop = ktime_get_raw();
- current_ns = ktime_to_ns(stop - start);
+ diff = ktime_sub(stop, start);
+
+ current_ns = ktime_to_ns(diff);
if (i == 0) {
lowest_ns = current_ns;
@@ -881,14 +967,14 @@ static int set_tod_write_overhead(struct idtcm_channel *channel)
return err;
}
-static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
+static int _idtcm_adjtime_deprecated(struct idtcm_channel *channel, s64 delta)
{
int err;
struct idtcm *idtcm = channel->idtcm;
struct timespec64 ts;
s64 now;
- if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
+ if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED) {
err = idtcm_do_phase_pull_in(channel, delta, 0);
} else {
idtcm->calculate_overhead_flag = 1;
@@ -908,7 +994,7 @@ static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
ts = ns_to_timespec64(now);
- err = _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
+ err = _idtcm_settime_deprecated(channel, &ts);
}
return err;
@@ -916,13 +1002,31 @@ static int _idtcm_adjtime(struct idtcm_channel *channel, s64 delta)
static int idtcm_state_machine_reset(struct idtcm *idtcm)
{
- int err;
u8 byte = SM_RESET_CMD;
+ u32 status = 0;
+ int err;
+ u8 i;
+
+ clear_boot_status(idtcm);
err = idtcm_write(idtcm, RESET_CTRL, SM_RESET, &byte, sizeof(byte));
- if (!err)
- msleep_interruptible(POST_SM_RESET_DELAY_MS);
+ if (!err) {
+ for (i = 0; i < 30; i++) {
+ msleep_interruptible(100);
+ read_boot_status(idtcm, &status);
+
+ if (status == 0xA0) {
+ dev_dbg(&idtcm->client->dev,
+ "SM_RESET completed in %d ms\n",
+ i * 100);
+ break;
+ }
+ }
+
+ if (!status)
+ dev_err(&idtcm->client->dev, "Timed out waiting for CM_RESET to complete\n");
+ }
return err;
}
@@ -1116,7 +1220,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm,
rec = (struct idtcm_fwrc *) fw->data;
- if (fw->size > 0)
+ if (contains_full_configuration(fw))
idtcm_state_machine_reset(idtcm);
for (len = fw->size; len > 0; len -= sizeof(*rec)) {
@@ -1168,11 +1272,19 @@ static int idtcm_output_enable(struct idtcm_channel *channel,
bool enable, unsigned int outn)
{
struct idtcm *idtcm = channel->idtcm;
+ int base;
int err;
u8 val;
- err = idtcm_read(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
- OUT_CTRL_1, &val, sizeof(val));
+ base = get_output_base_addr(outn);
+
+ if (!(base > 0)) {
+ dev_err(&idtcm->client->dev,
+ "%s - Unsupported out%d", __func__, outn);
+ return base;
+ }
+
+ err = idtcm_read(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
if (err)
return err;
@@ -1182,8 +1294,7 @@ static int idtcm_output_enable(struct idtcm_channel *channel,
else
val &= ~SQUELCH_DISABLE;
- return idtcm_write(idtcm, OUTPUT_MODULE_FROM_INDEX(outn),
- OUT_CTRL_1, &val, sizeof(val));
+ return idtcm_write(idtcm, (u16)base, OUT_CTRL_1, &val, sizeof(val));
}
static int idtcm_output_mask_enable(struct idtcm_channel *channel,
@@ -1226,6 +1337,23 @@ static int idtcm_perout_enable(struct idtcm_channel *channel,
return idtcm_output_enable(channel, enable, perout->index);
}
+static int idtcm_get_pll_mode(struct idtcm_channel *channel,
+ enum pll_mode *pll_mode)
+{
+ struct idtcm *idtcm = channel->idtcm;
+ int err;
+ u8 dpll_mode;
+
+ err = idtcm_read(idtcm, channel->dpll_n, DPLL_MODE,
+ &dpll_mode, sizeof(dpll_mode));
+ if (err)
+ return err;
+
+ *pll_mode = (dpll_mode >> PLL_MODE_SHIFT) & PLL_MODE_MASK;
+
+ return 0;
+}
+
static int idtcm_set_pll_mode(struct idtcm_channel *channel,
enum pll_mode pll_mode)
{
@@ -1277,16 +1405,8 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
if (err)
return err;
-
- channel->write_phase_ready = 0;
-
- ptp_schedule_worker(channel->ptp_clock,
- msecs_to_jiffies(WR_PHASE_SETUP_MS));
}
- if (!channel->write_phase_ready)
- delta_ns = 0;
-
offset_ps = (s64)delta_ns * 1000;
/*
@@ -1299,7 +1419,7 @@ static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
- phase_50ps = DIV_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
+ phase_50ps = div_s64(offset_ps, 50);
for (i = 0; i < 4; i++) {
buf[i] = phase_50ps & 0xff;
@@ -1316,7 +1436,6 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
{
struct idtcm *idtcm = channel->idtcm;
u8 i;
- bool neg_adj = 0;
int err;
u8 buf[6] = {0};
s64 fcw;
@@ -1340,18 +1459,11 @@ static int _idtcm_adjfine(struct idtcm_channel *channel, long scaled_ppm)
* FCW = -------------
* 111 * 2^4
*/
- if (scaled_ppm < 0) {
- neg_adj = 1;
- scaled_ppm = -scaled_ppm;
- }
/* 2 ^ -53 = 1.1102230246251565404236316680908e-16 */
fcw = scaled_ppm * 244140625ULL;
- fcw = div_u64(fcw, 1776);
-
- if (neg_adj)
- fcw = -fcw;
+ fcw = div_s64(fcw, 1776);
for (i = 0; i < 6; i++) {
buf[i] = fcw & 0xff;
@@ -1386,8 +1498,8 @@ static int idtcm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
return err;
}
-static int idtcm_settime(struct ptp_clock_info *ptp,
- const struct timespec64 *ts)
+static int idtcm_settime_deprecated(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1396,7 +1508,7 @@ static int idtcm_settime(struct ptp_clock_info *ptp,
mutex_lock(&idtcm->reg_lock);
- err = _idtcm_settime(channel, ts, HW_TOD_WR_TRIG_SEL_MSB);
+ err = _idtcm_settime_deprecated(channel, ts);
if (err)
dev_err(&idtcm->client->dev,
@@ -1409,7 +1521,7 @@ static int idtcm_settime(struct ptp_clock_info *ptp,
return err;
}
-static int idtcm_settime_v487(struct ptp_clock_info *ptp,
+static int idtcm_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct idtcm_channel *channel =
@@ -1419,7 +1531,7 @@ static int idtcm_settime_v487(struct ptp_clock_info *ptp,
mutex_lock(&idtcm->reg_lock);
- err = _idtcm_settime_v487(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
+ err = _idtcm_settime(channel, ts, SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
if (err)
dev_err(&idtcm->client->dev,
@@ -1432,7 +1544,7 @@ static int idtcm_settime_v487(struct ptp_clock_info *ptp,
return err;
}
-static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
+static int idtcm_adjtime_deprecated(struct ptp_clock_info *ptp, s64 delta)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1441,7 +1553,7 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
mutex_lock(&idtcm->reg_lock);
- err = _idtcm_adjtime(channel, delta);
+ err = _idtcm_adjtime_deprecated(channel, delta);
if (err)
dev_err(&idtcm->client->dev,
@@ -1454,7 +1566,7 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
return err;
}
-static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
+static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct idtcm_channel *channel =
container_of(ptp, struct idtcm_channel, caps);
@@ -1463,7 +1575,7 @@ static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
enum scsr_tod_write_type_sel type;
int err;
- if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS_V487) {
+ if (abs(delta) < PHASE_PULL_IN_THRESHOLD_NS) {
err = idtcm_do_phase_pull_in(channel, delta, 0);
if (err)
dev_err(&idtcm->client->dev,
@@ -1483,7 +1595,7 @@ static int idtcm_adjtime_v487(struct ptp_clock_info *ptp, s64 delta)
mutex_lock(&idtcm->reg_lock);
- err = _idtcm_settime_v487(channel, &ts, type);
+ err = _idtcm_settime(channel, &ts, type);
if (err)
dev_err(&idtcm->client->dev,
@@ -1827,10 +1939,14 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
if (err)
return err;
- return _idtcm_settime(channel, &ts, HW_TOD_WR_TRIG_SEL_MSB);
+ if (idtcm->deprecated)
+ return _idtcm_settime_deprecated(channel, &ts);
+ else
+ return _idtcm_settime(channel, &ts,
+ SCSR_TOD_WR_TYPE_SEL_ABSOLUTE);
}
-static void idtcm_display_version_info(struct idtcm *idtcm)
+static void idtcm_set_version_info(struct idtcm *idtcm)
{
u8 major;
u8 minor;
@@ -1852,34 +1968,37 @@ static void idtcm_display_version_info(struct idtcm *idtcm)
snprintf(idtcm->version, sizeof(idtcm->version), "%u.%u.%u",
major, minor, hotfix);
+ if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
+ idtcm->deprecated = 0;
+ else
+ idtcm->deprecated = 1;
+
dev_info(&idtcm->client->dev, fmt, major, minor, hotfix,
product_id, hw_rev_id, config_select);
}
-static const struct ptp_clock_info idtcm_caps_v487 = {
+static const struct ptp_clock_info idtcm_caps = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 12,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
- .adjtime = &idtcm_adjtime_v487,
+ .adjtime = &idtcm_adjtime,
.gettime64 = &idtcm_gettime,
- .settime64 = &idtcm_settime_v487,
+ .settime64 = &idtcm_settime,
.enable = &idtcm_enable,
- .do_aux_work = &set_write_phase_ready,
};
-static const struct ptp_clock_info idtcm_caps = {
+static const struct ptp_clock_info idtcm_caps_deprecated = {
.owner = THIS_MODULE,
.max_adj = 244000,
.n_per_out = 12,
.adjphase = &idtcm_adjphase,
.adjfine = &idtcm_adjfine,
- .adjtime = &idtcm_adjtime,
+ .adjtime = &idtcm_adjtime_deprecated,
.gettime64 = &idtcm_gettime,
- .settime64 = &idtcm_settime,
+ .settime64 = &idtcm_settime_deprecated,
.enable = &idtcm_enable,
- .do_aux_work = &set_write_phase_ready,
};
static int configure_channel_pll(struct idtcm_channel *channel)
@@ -2001,15 +2120,15 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
channel->idtcm = idtcm;
- if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0)
- channel->caps = idtcm_caps_v487;
+ if (idtcm->deprecated)
+ channel->caps = idtcm_caps_deprecated;
else
channel->caps = idtcm_caps;
snprintf(channel->caps.name, sizeof(channel->caps.name),
"IDT CM TOD%u", index);
- if (idtcm_strverscmp(idtcm->version, "4.8.7") >= 0) {
+ if (!idtcm->deprecated) {
err = idtcm_enable_tod_sync(channel);
if (err) {
dev_err(&idtcm->client->dev,
@@ -2020,12 +2139,11 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
}
}
- err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_FREQUENCY);
+ /* Sync pll mode with hardware */
+ err = idtcm_get_pll_mode(channel, &channel->pll_mode);
if (err) {
dev_err(&idtcm->client->dev,
- "Failed at line %d in func %s!\n",
- __LINE__,
- __func__);
+ "Error: %s - Unable to read pll mode\n", __func__);
return err;
}
@@ -2049,8 +2167,6 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
if (!channel->ptp_clock)
return -ENOTSUPP;
- channel->write_phase_ready = 0;
-
dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
index, channel->ptp_clock->index);
@@ -2111,7 +2227,7 @@ static int idtcm_probe(struct i2c_client *client,
mutex_init(&idtcm->reg_lock);
mutex_lock(&idtcm->reg_lock);
- idtcm_display_version_info(idtcm);
+ idtcm_set_version_info(idtcm);
err = idtcm_load_firmware(idtcm, &client->dev);
@@ -2119,6 +2235,9 @@ static int idtcm_probe(struct i2c_client *client,
dev_warn(&idtcm->client->dev,
"loading firmware failed with %d\n", err);
+ if (wait_for_boot_status_ready(idtcm))
+ dev_warn(&idtcm->client->dev, "BOOT_STATUS != 0xA0\n");
+
if (idtcm->tod_mask) {
for (i = 0; i < MAX_TOD; i++) {
if (idtcm->tod_mask & (1 << i)) {
diff --git a/drivers/ptp/ptp_clockmatrix.h b/drivers/ptp/ptp_clockmatrix.h
index 82840d72364a..645de2c66b64 100644
--- a/drivers/ptp/ptp_clockmatrix.h
+++ b/drivers/ptp/ptp_clockmatrix.h
@@ -15,6 +15,7 @@
#define FW_FILENAME "idtcm.bin"
#define MAX_TOD (4)
#define MAX_PLL (8)
+#define MAX_OUTPUT (12)
#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
@@ -44,18 +45,20 @@
#define DEFAULT_TOD2_PTP_PLL (2)
#define DEFAULT_TOD3_PTP_PLL (3)
-#define POST_SM_RESET_DELAY_MS (3000)
-#define PHASE_PULL_IN_THRESHOLD_NS (150000)
-#define PHASE_PULL_IN_THRESHOLD_NS_V487 (15000)
-#define TOD_WRITE_OVERHEAD_COUNT_MAX (2)
-#define TOD_BYTE_COUNT (11)
-#define WR_PHASE_SETUP_MS (5000)
+#define POST_SM_RESET_DELAY_MS (3000)
+#define PHASE_PULL_IN_THRESHOLD_NS_DEPRECATED (150000)
+#define PHASE_PULL_IN_THRESHOLD_NS (15000)
+#define TOD_WRITE_OVERHEAD_COUNT_MAX (2)
+#define TOD_BYTE_COUNT (11)
-#define OUTPUT_MODULE_FROM_INDEX(index) (OUTPUT_0 + (index) * 0x10)
+#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
-#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
+#define IDTCM_MAX_WRITE_COUNT (512)
-#define IDTCM_MAX_WRITE_COUNT (512)
+#define FULL_FW_CFG_BYTES (SCRATCH - GPIO_USER_CONTROL)
+#define FULL_FW_CFG_SKIPPED_BYTES (((SCRATCH >> 7) \
+ - (GPIO_USER_CONTROL >> 7)) \
+ * 4) /* 4 bytes skipped every 0x80 */
/* Values of DPLL_N.DPLL_MODE.PLL_MODE */
enum pll_mode {
@@ -120,7 +123,7 @@ struct idtcm_channel {
enum pll_mode pll_mode;
u8 pll;
u16 output_mask;
- int write_phase_ready;
+ u8 output_phase_adj[MAX_OUTPUT][4];
};
struct idtcm {
@@ -129,6 +132,7 @@ struct idtcm {
u8 page_offset;
u8 tod_mask;
char version[16];
+ u8 deprecated;
/* Overhead calculation for adjtime */
u8 calculate_overhead_flag;
diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c
index 179f6c472e50..c1c959f7e52b 100644
--- a/drivers/ptp/ptp_idt82p33.c
+++ b/drivers/ptp/ptp_idt82p33.c
@@ -21,6 +21,7 @@ MODULE_DESCRIPTION("Driver for IDT 82p33xxx clock devices");
MODULE_AUTHOR("IDT support-1588 <IDT-support-1588@lm.renesas.com>");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FW_FILENAME);
/* Module Parameters */
static u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC;
@@ -77,11 +78,10 @@ static void idt82p33_timespec_to_byte_array(struct timespec64 const *ts,
}
}
-static int idt82p33_xfer(struct idt82p33 *idt82p33,
- unsigned char regaddr,
- unsigned char *buf,
- unsigned int count,
- int write)
+static int idt82p33_xfer_read(struct idt82p33 *idt82p33,
+ unsigned char regaddr,
+ unsigned char *buf,
+ unsigned int count)
{
struct i2c_client *client = idt82p33->client;
struct i2c_msg msg[2];
@@ -93,7 +93,7 @@ static int idt82p33_xfer(struct idt82p33 *idt82p33,
msg[0].buf = &regaddr;
msg[1].addr = client->addr;
- msg[1].flags = write ? 0 : I2C_M_RD;
+ msg[1].flags = I2C_M_RD;
msg[1].len = count;
msg[1].buf = buf;
@@ -109,6 +109,31 @@ static int idt82p33_xfer(struct idt82p33 *idt82p33,
return 0;
}
+static int idt82p33_xfer_write(struct idt82p33 *idt82p33,
+ u8 regaddr,
+ u8 *buf,
+ u16 count)
+{
+ struct i2c_client *client = idt82p33->client;
+ /* we add 1 byte for device register */
+ u8 msg[IDT82P33_MAX_WRITE_COUNT + 1];
+ int err;
+
+ if (count > IDT82P33_MAX_WRITE_COUNT)
+ return -EINVAL;
+
+ msg[0] = regaddr;
+ memcpy(&msg[1], buf, count);
+
+ err = i2c_master_send(client, msg, count + 1);
+ if (err < 0) {
+ dev_err(&client->dev, "i2c_master_send returned %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
static int idt82p33_page_offset(struct idt82p33 *idt82p33, unsigned char val)
{
int err;
@@ -116,7 +141,7 @@ static int idt82p33_page_offset(struct idt82p33 *idt82p33, unsigned char val)
if (idt82p33->page_offset == val)
return 0;
- err = idt82p33_xfer(idt82p33, PAGE_ADDR, &val, sizeof(val), 1);
+ err = idt82p33_xfer_write(idt82p33, PAGE_ADDR, &val, sizeof(val));
if (err)
dev_err(&idt82p33->client->dev,
"failed to set page offset %d\n", val);
@@ -137,11 +162,12 @@ static int idt82p33_rdwr(struct idt82p33 *idt82p33, unsigned int regaddr,
err = idt82p33_page_offset(idt82p33, page);
if (err)
- goto out;
+ return err;
- err = idt82p33_xfer(idt82p33, offset, buf, count, write);
-out:
- return err;
+ if (write)
+ return idt82p33_xfer_write(idt82p33, offset, buf, count);
+
+ return idt82p33_xfer_read(idt82p33, offset, buf, count);
}
static int idt82p33_read(struct idt82p33 *idt82p33, unsigned int regaddr,
@@ -294,7 +320,6 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
unsigned char buf[5] = {0};
- int neg_adj = 0;
int err, i;
s64 fcw;
@@ -314,16 +339,9 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm)
* FCW = -------------
* 168 * 2^4
*/
- if (scaled_ppm < 0) {
- neg_adj = 1;
- scaled_ppm = -scaled_ppm;
- }
fcw = scaled_ppm * 244140625ULL;
- fcw = div_u64(fcw, 2688);
-
- if (neg_adj)
- fcw = -fcw;
+ fcw = div_s64(fcw, 2688);
for (i = 0; i < 5; i++) {
buf[i] = fcw & 0xff;
@@ -448,8 +466,11 @@ static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel)
err = idt82p33_measure_settime_gettime_gap_overhead(channel, &gap_ns);
- if (err)
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
return err;
+ }
err = idt82p33_measure_one_byte_write_overhead(channel,
&one_byte_write_ns);
@@ -518,13 +539,10 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable)
u8 sync_cnfg;
int err;
- if (enable == channel->sync_tod_on) {
- if (enable && sync_tod_timeout) {
- mod_delayed_work(system_wq, &channel->sync_tod_work,
- sync_tod_timeout * HZ);
- }
- return 0;
- }
+ /* Turn it off after sync_tod_timeout seconds */
+ if (enable && sync_tod_timeout)
+ ptp_schedule_worker(channel->ptp_clock,
+ sync_tod_timeout * HZ);
err = idt82p33_read(idt82p33, channel->dpll_sync_cnfg,
&sync_cnfg, sizeof(sync_cnfg));
@@ -532,29 +550,17 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable)
return err;
sync_cnfg &= ~SYNC_TOD;
-
if (enable)
sync_cnfg |= SYNC_TOD;
- err = idt82p33_write(idt82p33, channel->dpll_sync_cnfg,
- &sync_cnfg, sizeof(sync_cnfg));
- if (err)
- return err;
-
- channel->sync_tod_on = enable;
-
- if (enable && sync_tod_timeout) {
- mod_delayed_work(system_wq, &channel->sync_tod_work,
- sync_tod_timeout * HZ);
- }
-
- return 0;
+ return idt82p33_write(idt82p33, channel->dpll_sync_cnfg,
+ &sync_cnfg, sizeof(sync_cnfg));
}
-static void idt82p33_sync_tod_work_handler(struct work_struct *work)
+static long idt82p33_sync_tod_work_handler(struct ptp_clock_info *ptp)
{
struct idt82p33_channel *channel =
- container_of(work, struct idt82p33_channel, sync_tod_work.work);
+ container_of(ptp, struct idt82p33_channel, caps);
struct idt82p33 *idt82p33 = channel->idt82p33;
mutex_lock(&idt82p33->reg_lock);
@@ -562,35 +568,46 @@ static void idt82p33_sync_tod_work_handler(struct work_struct *work)
(void)idt82p33_sync_tod(channel, false);
mutex_unlock(&idt82p33->reg_lock);
+
+ /* Return a negative value here to not reschedule */
+ return -1;
}
-static int idt82p33_pps_enable(struct idt82p33_channel *channel, bool enable)
+static int idt82p33_output_enable(struct idt82p33_channel *channel,
+ bool enable, unsigned int outn)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
- u8 mask, outn, val;
int err;
+ u8 val;
+
+ err = idt82p33_read(idt82p33, OUT_MUX_CNFG(outn), &val, sizeof(val));
+ if (err)
+ return err;
+ if (enable)
+ val &= ~SQUELCH_ENABLE;
+ else
+ val |= SQUELCH_ENABLE;
+
+ return idt82p33_write(idt82p33, OUT_MUX_CNFG(outn), &val, sizeof(val));
+}
+
+static int idt82p33_output_mask_enable(struct idt82p33_channel *channel,
+ bool enable)
+{
+ u16 mask;
+ int err;
+ u8 outn;
mask = channel->output_mask;
outn = 0;
while (mask) {
if (mask & 0x1) {
- err = idt82p33_read(idt82p33, OUT_MUX_CNFG(outn),
- &val, sizeof(val));
- if (err)
- return err;
-
- if (enable)
- val &= ~SQUELCH_ENABLE;
- else
- val |= SQUELCH_ENABLE;
-
- err = idt82p33_write(idt82p33, OUT_MUX_CNFG(outn),
- &val, sizeof(val));
-
+ err = idt82p33_output_enable(channel, enable, outn);
if (err)
return err;
}
+
mask >>= 0x1;
outn++;
}
@@ -598,6 +615,20 @@ static int idt82p33_pps_enable(struct idt82p33_channel *channel, bool enable)
return 0;
}
+static int idt82p33_perout_enable(struct idt82p33_channel *channel,
+ bool enable,
+ struct ptp_perout_request *perout)
+{
+ unsigned int flags = perout->flags;
+
+ /* Enable/disable output based on output_mask */
+ if (flags == PEROUT_ENABLE_OUTPUT_MASK)
+ return idt82p33_output_mask_enable(channel, enable);
+
+ /* Enable/disable individual output instead */
+ return idt82p33_output_enable(channel, enable, perout->index);
+}
+
static int idt82p33_enable_tod(struct idt82p33_channel *channel)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
@@ -611,15 +642,13 @@ static int idt82p33_enable_tod(struct idt82p33_channel *channel)
if (err)
return err;
- err = idt82p33_pps_enable(channel, false);
-
- if (err)
- return err;
-
err = idt82p33_measure_tod_write_overhead(channel);
- if (err)
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
return err;
+ }
err = _idt82p33_settime(channel, &ts);
@@ -638,10 +667,8 @@ static void idt82p33_ptp_clock_unregister_all(struct idt82p33 *idt82p33)
channel = &idt82p33->channel[i];
- if (channel->ptp_clock) {
+ if (channel->ptp_clock)
ptp_clock_unregister(channel->ptp_clock);
- cancel_delayed_work_sync(&channel->sync_tod_work);
- }
}
}
@@ -659,14 +686,15 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
if (rq->type == PTP_CLK_REQ_PEROUT) {
if (!on)
- err = idt82p33_pps_enable(channel, false);
-
+ err = idt82p33_perout_enable(channel, false,
+ &rq->perout);
/* Only accept a 1-PPS aligned to the second. */
else if (rq->perout.start.nsec || rq->perout.period.sec != 1 ||
rq->perout.period.nsec) {
err = -ERANGE;
} else
- err = idt82p33_pps_enable(channel, true);
+ err = idt82p33_perout_enable(channel, true,
+ &rq->perout);
}
mutex_unlock(&idt82p33->reg_lock);
@@ -674,6 +702,48 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
return err;
}
+static int idt82p33_adjwritephase(struct ptp_clock_info *ptp, s32 offset_ns)
+{
+ struct idt82p33_channel *channel =
+ container_of(ptp, struct idt82p33_channel, caps);
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ s64 offset_regval, offset_fs;
+ u8 val[4] = {0};
+ int err;
+
+ offset_fs = (s64)(-offset_ns) * 1000000;
+
+ if (offset_fs > WRITE_PHASE_OFFSET_LIMIT)
+ offset_fs = WRITE_PHASE_OFFSET_LIMIT;
+ else if (offset_fs < -WRITE_PHASE_OFFSET_LIMIT)
+ offset_fs = -WRITE_PHASE_OFFSET_LIMIT;
+
+ /* Convert from phaseoffset_fs to register value */
+ offset_regval = div_s64(offset_fs * 1000, IDT_T0DPLL_PHASE_RESOL);
+
+ val[0] = offset_regval & 0xFF;
+ val[1] = (offset_regval >> 8) & 0xFF;
+ val[2] = (offset_regval >> 16) & 0xFF;
+ val[3] = (offset_regval >> 24) & 0x1F;
+ val[3] |= PH_OFFSET_EN;
+
+ mutex_lock(&idt82p33->reg_lock);
+
+ err = idt82p33_dpll_set_mode(channel, PLL_MODE_WPH);
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
+ goto out;
+ }
+
+ err = idt82p33_write(idt82p33, channel->dpll_phase_cnfg, val,
+ sizeof(val));
+
+out:
+ mutex_unlock(&idt82p33->reg_lock);
+ return err;
+}
+
static int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct idt82p33_channel *channel =
@@ -683,6 +753,9 @@ static int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
mutex_lock(&idt82p33->reg_lock);
err = _idt82p33_adjfine(channel, scaled_ppm);
+ if (err)
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
mutex_unlock(&idt82p33->reg_lock);
return err;
@@ -706,10 +779,15 @@ static int idt82p33_adjtime(struct ptp_clock_info *ptp, s64 delta_ns)
if (err) {
mutex_unlock(&idt82p33->reg_lock);
+ dev_err(&idt82p33->client->dev,
+ "Adjtime failed in %s with err %d!\n", __func__, err);
return err;
}
err = idt82p33_sync_tod(channel, true);
+ if (err)
+ dev_err(&idt82p33->client->dev,
+ "Sync_tod failed in %s with err %d!\n", __func__, err);
mutex_unlock(&idt82p33->reg_lock);
@@ -725,6 +803,9 @@ static int idt82p33_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
mutex_lock(&idt82p33->reg_lock);
err = _idt82p33_gettime(channel, ts);
+ if (err)
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
mutex_unlock(&idt82p33->reg_lock);
return err;
@@ -740,6 +821,9 @@ static int idt82p33_settime(struct ptp_clock_info *ptp,
mutex_lock(&idt82p33->reg_lock);
err = _idt82p33_settime(channel, ts);
+ if (err)
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
mutex_unlock(&idt82p33->reg_lock);
return err;
@@ -772,9 +856,6 @@ static int idt82p33_channel_init(struct idt82p33_channel *channel, int index)
return -EINVAL;
}
- INIT_DELAYED_WORK(&channel->sync_tod_work,
- idt82p33_sync_tod_work_handler);
- channel->sync_tod_on = false;
channel->current_freq_ppb = 0;
return 0;
@@ -784,11 +865,14 @@ static void idt82p33_caps_init(struct ptp_clock_info *caps)
{
caps->owner = THIS_MODULE;
caps->max_adj = 92000;
+ caps->n_per_out = 11;
+ caps->adjphase = idt82p33_adjwritephase;
caps->adjfine = idt82p33_adjfine;
caps->adjtime = idt82p33_adjtime;
caps->gettime64 = idt82p33_gettime;
caps->settime64 = idt82p33_settime;
caps->enable = idt82p33_enable;
+ caps->do_aux_work = idt82p33_sync_tod_work_handler;
}
static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
@@ -802,23 +886,18 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
channel = &idt82p33->channel[index];
err = idt82p33_channel_init(channel, index);
- if (err)
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Channel_init failed in %s with err %d!\n",
+ __func__, err);
return err;
+ }
channel->idt82p33 = idt82p33;
idt82p33_caps_init(&channel->caps);
snprintf(channel->caps.name, sizeof(channel->caps.name),
"IDT 82P33 PLL%u", index);
- channel->caps.n_per_out = hweight8(channel->output_mask);
-
- err = idt82p33_dpll_set_mode(channel, PLL_MODE_DCO);
- if (err)
- return err;
-
- err = idt82p33_enable_tod(channel);
- if (err)
- return err;
channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
@@ -831,6 +910,22 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
if (!channel->ptp_clock)
return -ENOTSUPP;
+ err = idt82p33_dpll_set_mode(channel, PLL_MODE_DCO);
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Dpll_set_mode failed in %s with err %d!\n",
+ __func__, err);
+ return err;
+ }
+
+ err = idt82p33_enable_tod(channel);
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Enable_tod failed in %s with err %d!\n",
+ __func__, err);
+ return err;
+ }
+
dev_info(&idt82p33->client->dev, "PLL%d registered as ptp%d\n",
index, channel->ptp_clock->index);
@@ -850,8 +945,11 @@ static int idt82p33_load_firmware(struct idt82p33 *idt82p33)
err = request_firmware(&fw, FW_FILENAME, &idt82p33->client->dev);
- if (err)
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n", __func__, err);
return err;
+ }
dev_dbg(&idt82p33->client->dev, "firmware size %zu bytes\n", fw->size);
@@ -935,8 +1033,12 @@ static int idt82p33_probe(struct i2c_client *client,
for (i = 0; i < MAX_PHC_PLL; i++) {
if (idt82p33->pll_mask & (1 << i)) {
err = idt82p33_enable_channel(idt82p33, i);
- if (err)
+ if (err) {
+ dev_err(&idt82p33->client->dev,
+ "Failed in %s with err %d!\n",
+ __func__, err);
break;
+ }
}
}
} else {
diff --git a/drivers/ptp/ptp_idt82p33.h b/drivers/ptp/ptp_idt82p33.h
index 9d46966d25f1..1c7a0f0872e8 100644
--- a/drivers/ptp/ptp_idt82p33.h
+++ b/drivers/ptp/ptp_idt82p33.h
@@ -56,6 +56,8 @@
#define PLL_MODE_SHIFT (0)
#define PLL_MODE_MASK (0x1F)
+#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
+
enum pll_mode {
PLL_MODE_MIN = 0,
PLL_MODE_AUTOMATIC = PLL_MODE_MIN,
@@ -93,6 +95,7 @@ enum hw_tod_trig_sel {
#define MAX_MEASURMENT_COUNT (5)
#define SNAP_THRESHOLD_NS (150000)
#define SYNC_TOD_TIMEOUT_SEC (5)
+#define IDT82P33_MAX_WRITE_COUNT (512)
#define PLLMASK_ADDR_HI 0xFF
#define PLLMASK_ADDR_LO 0xA5
diff --git a/drivers/ptp/ptp_ines.c b/drivers/ptp/ptp_ines.c
index 4700ffbdfced..6c7c2843ba0b 100644
--- a/drivers/ptp/ptp_ines.c
+++ b/drivers/ptp/ptp_ines.c
@@ -108,11 +108,6 @@ MODULE_LICENSE("GPL");
#define MESSAGE_TYPE_P_DELAY_RESP 3
#define MESSAGE_TYPE_DELAY_REQ 4
-#define SYNC 0x0
-#define DELAY_REQ 0x1
-#define PDELAY_REQ 0x2
-#define PDELAY_RESP 0x3
-
static LIST_HEAD(ines_clocks);
static DEFINE_MUTEX(ines_clocks_lock);
@@ -683,9 +678,9 @@ static bool is_sync_pdelay_resp(struct sk_buff *skb, int type)
msgtype = ptp_get_msgtype(hdr, type);
- switch ((msgtype & 0xf)) {
- case SYNC:
- case PDELAY_RESP:
+ switch (msgtype) {
+ case PTP_MSGTYPE_SYNC:
+ case PTP_MSGTYPE_PDELAY_RESP:
return true;
default:
return false;
@@ -696,13 +691,13 @@ static u8 tag_to_msgtype(u8 tag)
{
switch (tag) {
case MESSAGE_TYPE_SYNC:
- return SYNC;
+ return PTP_MSGTYPE_SYNC;
case MESSAGE_TYPE_P_DELAY_REQ:
- return PDELAY_REQ;
+ return PTP_MSGTYPE_PDELAY_REQ;
case MESSAGE_TYPE_P_DELAY_RESP:
- return PDELAY_RESP;
+ return PTP_MSGTYPE_PDELAY_RESP;
case MESSAGE_TYPE_DELAY_REQ:
- return DELAY_REQ;
+ return PTP_MSGTYPE_DELAY_REQ;
}
return 0xf;
}
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
new file mode 100644
index 000000000000..530e5f90095e
--- /dev/null
+++ b/drivers/ptp/ptp_ocp.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020 Facebook */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ptp_clock_kernel.h>
+
+static const struct pci_device_id ptp_ocp_pcidev_id[] = {
+ { PCI_DEVICE(0x1d9b, 0x0400) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
+
+#define OCP_REGISTER_OFFSET 0x01000000
+
+struct ocp_reg {
+ u32 ctrl;
+ u32 status;
+ u32 select;
+ u32 version;
+ u32 time_ns;
+ u32 time_sec;
+ u32 __pad0[2];
+ u32 adjust_ns;
+ u32 adjust_sec;
+ u32 __pad1[2];
+ u32 offset_ns;
+ u32 offset_window_ns;
+};
+
+#define OCP_CTRL_ENABLE BIT(0)
+#define OCP_CTRL_ADJUST_TIME BIT(1)
+#define OCP_CTRL_ADJUST_OFFSET BIT(2)
+#define OCP_CTRL_READ_TIME_REQ BIT(30)
+#define OCP_CTRL_READ_TIME_DONE BIT(31)
+
+#define OCP_STATUS_IN_SYNC BIT(0)
+
+#define OCP_SELECT_CLK_NONE 0
+#define OCP_SELECT_CLK_REG 6
+
+struct tod_reg {
+ u32 ctrl;
+ u32 status;
+ u32 uart_polarity;
+ u32 version;
+ u32 correction_sec;
+ u32 __pad0[3];
+ u32 uart_baud;
+ u32 __pad1[3];
+ u32 utc_status;
+ u32 leap;
+};
+
+#define TOD_REGISTER_OFFSET 0x01050000
+
+#define TOD_CTRL_PROTOCOL BIT(28)
+#define TOD_CTRL_DISABLE_FMT_A BIT(17)
+#define TOD_CTRL_DISABLE_FMT_B BIT(16)
+#define TOD_CTRL_ENABLE BIT(0)
+#define TOD_CTRL_GNSS_MASK ((1U << 4) - 1)
+#define TOD_CTRL_GNSS_SHIFT 24
+
+#define TOD_STATUS_UTC_MASK 0xff
+#define TOD_STATUS_UTC_VALID BIT(8)
+#define TOD_STATUS_LEAP_VALID BIT(16)
+
+struct ptp_ocp {
+ struct pci_dev *pdev;
+ spinlock_t lock;
+ void __iomem *base;
+ struct ocp_reg __iomem *reg;
+ struct tod_reg __iomem *tod;
+ struct ptp_clock *ptp;
+ struct ptp_clock_info ptp_info;
+};
+
+static int
+__ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ u32 ctrl, time_sec, time_ns;
+ int i;
+
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_READ_TIME_REQ;
+
+ ptp_read_system_prets(sts);
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ for (i = 0; i < 100; i++) {
+ ctrl = ioread32(&bp->reg->ctrl);
+ if (ctrl & OCP_CTRL_READ_TIME_DONE)
+ break;
+ }
+ ptp_read_system_postts(sts);
+
+ time_ns = ioread32(&bp->reg->time_ns);
+ time_sec = ioread32(&bp->reg->time_sec);
+
+ ts->tv_sec = time_sec;
+ ts->tv_nsec = time_ns;
+
+ return ctrl & OCP_CTRL_READ_TIME_DONE ? 0 : -ETIMEDOUT;
+}
+
+static int
+ptp_ocp_gettimex(struct ptp_clock_info *ptp_info, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ err = __ptp_ocp_gettime_locked(bp, ts, sts);
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return err;
+}
+
+static void
+__ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts)
+{
+ u32 ctrl, time_sec, time_ns;
+ u32 select;
+
+ time_ns = ts->tv_nsec;
+ time_sec = ts->tv_sec;
+
+ select = ioread32(&bp->reg->select);
+ iowrite32(OCP_SELECT_CLK_REG, &bp->reg->select);
+
+ iowrite32(time_ns, &bp->reg->adjust_ns);
+ iowrite32(time_sec, &bp->reg->adjust_sec);
+
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_ADJUST_TIME;
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ /* restore clock selection */
+ iowrite32(select >> 16, &bp->reg->select);
+}
+
+static int
+ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts)
+{
+ struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
+ unsigned long flags;
+
+ if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC)
+ return 0;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ __ptp_ocp_settime_locked(bp, ts);
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
+static int
+ptp_ocp_adjtime(struct ptp_clock_info *ptp_info, s64 delta_ns)
+{
+ struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
+ struct timespec64 ts;
+ unsigned long flags;
+ int err;
+
+ if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC)
+ return 0;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ err = __ptp_ocp_gettime_locked(bp, &ts, NULL);
+ if (likely(!err)) {
+ timespec64_add_ns(&ts, delta_ns);
+ __ptp_ocp_settime_locked(bp, &ts);
+ }
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return err;
+}
+
+static int
+ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
+{
+ if (scaled_ppm == 0)
+ return 0;
+
+ return -EOPNOTSUPP;
+}
+
+static const struct ptp_clock_info ptp_ocp_clock_info = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .max_adj = 100000000,
+ .gettimex64 = ptp_ocp_gettimex,
+ .settime64 = ptp_ocp_settime,
+ .adjtime = ptp_ocp_adjtime,
+ .adjfine = ptp_ocp_null_adjfine,
+};
+
+static int
+ptp_ocp_check_clock(struct ptp_ocp *bp)
+{
+ struct timespec64 ts;
+ bool sync;
+ u32 ctrl;
+
+ /* make sure clock is enabled */
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_ENABLE;
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ if ((ioread32(&bp->reg->ctrl) & OCP_CTRL_ENABLE) == 0) {
+ dev_err(&bp->pdev->dev, "clock not enabled\n");
+ return -ENODEV;
+ }
+
+ sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+ if (!sync) {
+ ktime_get_real_ts64(&ts);
+ ptp_ocp_settime(&bp->ptp_info, &ts);
+ }
+ if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL))
+ dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n",
+ ts.tv_sec, ts.tv_nsec,
+ sync ? "in-sync" : "UNSYNCED");
+
+ return 0;
+}
+
+static void
+ptp_ocp_tod_info(struct ptp_ocp *bp)
+{
+ static const char * const proto_name[] = {
+ "NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none",
+ "UBX", "UBX_UTC", "UBX_LS", "UBX_none"
+ };
+ static const char * const gnss_name[] = {
+ "ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU",
+ };
+ u32 version, ctrl, reg;
+ int idx;
+
+ version = ioread32(&bp->tod->version);
+ dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n",
+ version >> 24, (version >> 16) & 0xff, version & 0xffff);
+
+ ctrl = ioread32(&bp->tod->ctrl);
+ ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE;
+ ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B);
+ iowrite32(ctrl, &bp->tod->ctrl);
+
+ ctrl = ioread32(&bp->tod->ctrl);
+ idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0;
+ idx += (ctrl >> 16) & 3;
+ dev_info(&bp->pdev->dev, "control: %x\n", ctrl);
+ dev_info(&bp->pdev->dev, "TOD Protocol %s %s\n", proto_name[idx],
+ ctrl & TOD_CTRL_ENABLE ? "enabled" : "");
+
+ idx = (ctrl >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK;
+ if (idx < ARRAY_SIZE(gnss_name))
+ dev_info(&bp->pdev->dev, "GNSS %s\n", gnss_name[idx]);
+
+ reg = ioread32(&bp->tod->status);
+ dev_info(&bp->pdev->dev, "status: %x\n", reg);
+
+ reg = ioread32(&bp->tod->correction_sec);
+ dev_info(&bp->pdev->dev, "correction: %d\n", reg);
+
+ reg = ioread32(&bp->tod->utc_status);
+ dev_info(&bp->pdev->dev, "utc_status: %x\n", reg);
+ dev_info(&bp->pdev->dev, "utc_offset: %d valid:%d leap_valid:%d\n",
+ reg & TOD_STATUS_UTC_MASK, reg & TOD_STATUS_UTC_VALID ? 1 : 0,
+ reg & TOD_STATUS_LEAP_VALID ? 1 : 0);
+}
+
+static void
+ptp_ocp_info(struct ptp_ocp *bp)
+{
+ static const char * const clock_name[] = {
+ "NO", "TOD", "IRIG", "PPS", "PTP", "RTC", "REGS", "EXT"
+ };
+ u32 version, select;
+
+ version = ioread32(&bp->reg->version);
+ select = ioread32(&bp->reg->select);
+ dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n",
+ version >> 24, (version >> 16) & 0xff, version & 0xffff,
+ clock_name[select & 7],
+ ptp_clock_index(bp->ptp));
+
+ ptp_ocp_tod_info(bp);
+}
+
+static int
+ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ptp_ocp *bp;
+ int err;
+
+ bp = kzalloc(sizeof(*bp), GFP_KERNEL);
+ if (!bp)
+ return -ENOMEM;
+ bp->pdev = pdev;
+ pci_set_drvdata(pdev, bp);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_device\n");
+ goto out_free;
+ }
+
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (err) {
+ dev_err(&pdev->dev, "pci_request_region\n");
+ goto out_disable;
+ }
+
+ bp->base = pci_ioremap_bar(pdev, 0);
+ if (!bp->base) {
+ dev_err(&pdev->dev, "io_remap bar0\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ bp->reg = bp->base + OCP_REGISTER_OFFSET;
+ bp->tod = bp->base + TOD_REGISTER_OFFSET;
+ bp->ptp_info = ptp_ocp_clock_info;
+ spin_lock_init(&bp->lock);
+
+ err = ptp_ocp_check_clock(bp);
+ if (err)
+ goto out;
+
+ bp->ptp = ptp_clock_register(&bp->ptp_info, &pdev->dev);
+ if (IS_ERR(bp->ptp)) {
+ dev_err(&pdev->dev, "ptp_clock_register\n");
+ err = PTR_ERR(bp->ptp);
+ goto out;
+ }
+
+ ptp_ocp_info(bp);
+
+ return 0;
+
+out:
+ pci_release_regions(pdev);
+out_disable:
+ pci_disable_device(pdev);
+out_free:
+ kfree(bp);
+
+ return err;
+}
+
+static void
+ptp_ocp_remove(struct pci_dev *pdev)
+{
+ struct ptp_ocp *bp = pci_get_drvdata(pdev);
+
+ ptp_clock_unregister(bp->ptp);
+ pci_iounmap(pdev, bp->base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ kfree(bp);
+}
+
+static struct pci_driver ptp_ocp_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ptp_ocp_pcidev_id,
+ .probe = ptp_ocp_probe,
+ .remove = ptp_ocp_remove,
+};
+
+static int __init
+ptp_ocp_init(void)
+{
+ int err;
+
+ err = pci_register_driver(&ptp_ocp_driver);
+ return err;
+}
+
+static void __exit
+ptp_ocp_fini(void)
+{
+ pci_unregister_driver(&ptp_ocp_driver);
+}
+
+module_init(ptp_ocp_init);
+module_exit(ptp_ocp_fini);
+
+MODULE_DESCRIPTION("OpenCompute TimeCard driver");
+MODULE_LICENSE("GPL v2");