Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ptp: ocp: Add support for Xilinx-based Adva TimeCard variant

Add support for the Adva TimeCard model built on a Xilinx-based design.
This patch enables detection and integration of the new hardware within
the existing OCP timecard framework.
The Xilinx variant relies on the shared driver infrastructure, requiring
only small, targeted additions to accommodate its specific
characteristics.

Signed-off-by: Sagi Maimon <maimon.sagi@gmail.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20260312082009.249622-1-maimon.sagi@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Sagi Maimon and committed by
Jakub Kicinski
9089c5f3 d6e0f04b

+337 -28
+337 -28
drivers/ptp/ptp_ocp.c
··· 35 35 36 36 #define PCI_VENDOR_ID_ADVA 0xad5a 37 37 #define PCI_DEVICE_ID_ADVA_TIMECARD 0x0400 38 + #define PCI_DEVICE_ID_ADVA_TIMECARD_X1 0x0410 38 39 39 40 static struct class timecard_class = { 40 41 .name = "timecard", ··· 71 70 u32 servo_offset_i; 72 71 u32 servo_drift_p; 73 72 u32 servo_drift_i; 73 + }; 74 + 75 + /* 76 + * Combined servo + board-variant parameters for ADVA boards. 77 + * Embedded in the resource table .extra so a single ptp_ocp_adva_board_init() 78 + * can handle both ADVA and ADVA-X1 without per-variant init functions. 79 + */ 80 + struct ptp_ocp_adva_info { 81 + struct ptp_ocp_servo_conf servo; 82 + u32 flash_start; 83 + const struct ocp_sma_op *sma_op; 84 + u8 signals_nr; 85 + u8 freq_in_nr; 86 + const struct ocp_attr_group *attr_groups; 74 87 }; 75 88 76 89 #define OCP_CTRL_ENABLE BIT(0) ··· 308 293 const struct attribute_group *group; 309 294 }; 310 295 296 + struct ocp_selector { 297 + const char *name; 298 + int value; 299 + u64 frequency; 300 + }; 301 + 302 + struct ocp_sma_op { 303 + const struct ocp_selector *tbl[2]; 304 + void (*init)(struct ptp_ocp *bp); 305 + u32 (*get)(struct ptp_ocp *bp, int sma_nr); 306 + int (*set_inputs)(struct ptp_ocp *bp, int sma_nr, u32 val); 307 + int (*set_output)(struct ptp_ocp *bp, int sma_nr, u32 val); 308 + }; 309 + 311 310 #define OCP_CAP_BASIC BIT(0) 312 311 #define OCP_CAP_SIGNAL BIT(1) 313 312 #define OCP_CAP_FREQ BIT(2) ··· 449 420 450 421 static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r); 451 422 423 + static const struct ocp_sma_op ocp_adva_sma_op; 424 + static const struct ocp_sma_op ocp_adva_x1_sma_op; 425 + 452 426 static const struct ocp_attr_group fb_timecard_groups[]; 453 427 454 428 static const struct ocp_attr_group art_timecard_groups[]; 455 429 456 430 static const struct ocp_attr_group adva_timecard_groups[]; 431 + 432 + static const struct ocp_attr_group adva_timecard_x1_groups[]; 457 433 458 434 struct ptp_ocp_eeprom_map { 459 435 u16 off; ··· 1054 1020 }, 1055 1021 { 1056 1022 .setup = ptp_ocp_adva_board_init, 1057 - .extra = &(struct ptp_ocp_servo_conf) { 1058 - .servo_offset_p = 0xc000, 1059 - .servo_offset_i = 0x1000, 1060 - .servo_drift_p = 0, 1061 - .servo_drift_i = 0, 1023 + .extra = &(struct ptp_ocp_adva_info) { 1024 + .servo = { 1025 + .servo_offset_p = 0xc000, 1026 + .servo_offset_i = 0x1000, 1027 + .servo_drift_p = 0, 1028 + .servo_drift_i = 0, 1029 + }, 1030 + .flash_start = 0xA00000, 1031 + .sma_op = &ocp_adva_sma_op, 1032 + .signals_nr = 2, 1033 + .freq_in_nr = 2, 1034 + .attr_groups = adva_timecard_groups, 1035 + }, 1036 + }, 1037 + { } 1038 + }; 1039 + 1040 + static struct ocp_resource ocp_adva_x1_resource[] = { 1041 + { 1042 + OCP_MEM_RESOURCE(reg), 1043 + .offset = 0x01000000, .size = 0x10000, 1044 + }, 1045 + { 1046 + OCP_EXT_RESOURCE(ts0), 1047 + .offset = 0x01010000, .size = 0x10000, .irq_vec = 1, 1048 + .extra = &(struct ptp_ocp_ext_info) { 1049 + .index = 0, 1050 + .irq_fcn = ptp_ocp_ts_irq, 1051 + .enable = ptp_ocp_ts_enable, 1052 + }, 1053 + }, 1054 + { 1055 + OCP_EXT_RESOURCE(ts1), 1056 + .offset = 0x01020000, .size = 0x10000, .irq_vec = 2, 1057 + .extra = &(struct ptp_ocp_ext_info) { 1058 + .index = 1, 1059 + .irq_fcn = ptp_ocp_ts_irq, 1060 + .enable = ptp_ocp_ts_enable, 1061 + }, 1062 + }, 1063 + { 1064 + OCP_EXT_RESOURCE(ts2), 1065 + .offset = 0x01060000, .size = 0x10000, .irq_vec = 6, 1066 + .extra = &(struct ptp_ocp_ext_info) { 1067 + .index = 2, 1068 + .irq_fcn = ptp_ocp_ts_irq, 1069 + .enable = ptp_ocp_ts_enable, 1070 + }, 1071 + }, 1072 + { 1073 + OCP_EXT_RESOURCE(ts3), 1074 + .offset = 0x01110000, .size = 0x10000, .irq_vec = 15, 1075 + .extra = &(struct ptp_ocp_ext_info) { 1076 + .index = 3, 1077 + .irq_fcn = ptp_ocp_ts_irq, 1078 + .enable = ptp_ocp_ts_enable, 1079 + }, 1080 + }, 1081 + { 1082 + OCP_EXT_RESOURCE(ts4), 1083 + .offset = 0x01120000, .size = 0x10000, .irq_vec = 16, 1084 + .extra = &(struct ptp_ocp_ext_info) { 1085 + .index = 4, 1086 + .irq_fcn = ptp_ocp_ts_irq, 1087 + .enable = ptp_ocp_ts_enable, 1088 + }, 1089 + }, 1090 + /* Timestamp for PHC and/or PPS generator */ 1091 + { 1092 + OCP_EXT_RESOURCE(pps), 1093 + .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, 1094 + .extra = &(struct ptp_ocp_ext_info) { 1095 + .index = 5, 1096 + .irq_fcn = ptp_ocp_ts_irq, 1097 + .enable = ptp_ocp_ts_enable, 1098 + }, 1099 + }, 1100 + { 1101 + OCP_EXT_RESOURCE(signal_out[0]), 1102 + .offset = 0x010D0000, .size = 0x10000, .irq_vec = 11, 1103 + .extra = &(struct ptp_ocp_ext_info) { 1104 + .index = 1, 1105 + .irq_fcn = ptp_ocp_signal_irq, 1106 + .enable = ptp_ocp_signal_enable, 1107 + }, 1108 + }, 1109 + { 1110 + OCP_EXT_RESOURCE(signal_out[1]), 1111 + .offset = 0x010E0000, .size = 0x10000, .irq_vec = 12, 1112 + .extra = &(struct ptp_ocp_ext_info) { 1113 + .index = 2, 1114 + .irq_fcn = ptp_ocp_signal_irq, 1115 + .enable = ptp_ocp_signal_enable, 1116 + }, 1117 + }, 1118 + { 1119 + OCP_EXT_RESOURCE(signal_out[2]), 1120 + .offset = 0x010F0000, .size = 0x10000, .irq_vec = 13, 1121 + .extra = &(struct ptp_ocp_ext_info) { 1122 + .index = 3, 1123 + .irq_fcn = ptp_ocp_signal_irq, 1124 + .enable = ptp_ocp_signal_enable, 1125 + }, 1126 + }, 1127 + { 1128 + OCP_EXT_RESOURCE(signal_out[3]), 1129 + .offset = 0x01100000, .size = 0x10000, .irq_vec = 14, 1130 + .extra = &(struct ptp_ocp_ext_info) { 1131 + .index = 4, 1132 + .irq_fcn = ptp_ocp_signal_irq, 1133 + .enable = ptp_ocp_signal_enable, 1134 + }, 1135 + }, 1136 + { 1137 + OCP_MEM_RESOURCE(pps_to_ext), 1138 + .offset = 0x01030000, .size = 0x10000, 1139 + }, 1140 + { 1141 + OCP_MEM_RESOURCE(pps_to_clk), 1142 + .offset = 0x01040000, .size = 0x10000, 1143 + }, 1144 + { 1145 + OCP_MEM_RESOURCE(tod), 1146 + .offset = 0x01050000, .size = 0x10000, 1147 + }, 1148 + { 1149 + OCP_MEM_RESOURCE(image), 1150 + .offset = 0x00020000, .size = 0x1000, 1151 + }, 1152 + { 1153 + OCP_MEM_RESOURCE(pps_select), 1154 + .offset = 0x00130000, .size = 0x1000, 1155 + }, 1156 + { 1157 + OCP_MEM_RESOURCE(sma_map1), 1158 + .offset = 0x00140000, .size = 0x1000, 1159 + }, 1160 + { 1161 + OCP_MEM_RESOURCE(sma_map2), 1162 + .offset = 0x00220000, .size = 0x1000, 1163 + }, 1164 + { 1165 + OCP_SERIAL_RESOURCE(port[PORT_GNSS]), 1166 + .offset = 0x00160000 + 0x1000, .irq_vec = 3, 1167 + .extra = &(struct ptp_ocp_serial_port) { 1168 + .baud = 9600, 1169 + }, 1170 + }, 1171 + { 1172 + OCP_SERIAL_RESOURCE(port[PORT_MAC]), 1173 + .offset = 0x00180000 + 0x1000, .irq_vec = 5, 1174 + .extra = &(struct ptp_ocp_serial_port) { 1175 + .baud = 115200, 1176 + }, 1177 + }, 1178 + { 1179 + OCP_MEM_RESOURCE(freq_in[0]), 1180 + .offset = 0x01200000, .size = 0x10000, 1181 + }, 1182 + { 1183 + OCP_MEM_RESOURCE(freq_in[1]), 1184 + .offset = 0x01210000, .size = 0x10000, 1185 + }, 1186 + { 1187 + OCP_MEM_RESOURCE(freq_in[2]), 1188 + .offset = 0x01220000, .size = 0x10000, 1189 + }, 1190 + { 1191 + OCP_MEM_RESOURCE(freq_in[3]), 1192 + .offset = 0x01230000, .size = 0x10000, 1193 + }, 1194 + { 1195 + OCP_SPI_RESOURCE(spi_flash), 1196 + .offset = 0x00310000, .size = 0x10000, .irq_vec = 9, 1197 + .extra = &(struct ptp_ocp_flash_info) { 1198 + .name = "xilinx_spi", .pci_offset = 0, 1199 + .data_size = sizeof(struct xspi_platform_data), 1200 + .data = &(struct xspi_platform_data) { 1201 + .num_chipselect = 1, 1202 + .bits_per_word = 8, 1203 + .num_devices = 1, 1204 + .force_irq = true, 1205 + .devices = &(struct spi_board_info) { 1206 + .modalias = "spi-nor", 1207 + }, 1208 + }, 1209 + }, 1210 + }, 1211 + { 1212 + OCP_I2C_RESOURCE(i2c_ctrl), 1213 + .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, 1214 + .extra = &(struct ptp_ocp_i2c_info) { 1215 + .name = "xiic-i2c", 1216 + .fixed_rate = 50000000, 1217 + .data_size = sizeof(struct xiic_i2c_platform_data), 1218 + .data = &(struct xiic_i2c_platform_data) { 1219 + .num_devices = 2, 1220 + .devices = (struct i2c_board_info[]) { 1221 + { I2C_BOARD_INFO("24c02", 0x50) }, 1222 + { I2C_BOARD_INFO("24mac402", 0x58), 1223 + .platform_data = "mac" }, 1224 + }, 1225 + }, 1226 + }, 1227 + }, 1228 + { 1229 + .setup = ptp_ocp_adva_board_init, 1230 + .extra = &(struct ptp_ocp_adva_info) { 1231 + .servo = { 1232 + .servo_offset_p = 0xc000, 1233 + .servo_offset_i = 0x1000, 1234 + .servo_drift_p = 0, 1235 + .servo_drift_i = 0, 1236 + }, 1237 + .flash_start = 0x1000000, 1238 + .sma_op = &ocp_adva_x1_sma_op, 1239 + .signals_nr = 4, 1240 + .freq_in_nr = 4, 1241 + .attr_groups = adva_timecard_x1_groups, 1062 1242 }, 1063 1243 }, 1064 1244 { } ··· 1283 1035 { PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) }, 1284 1036 { PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) }, 1285 1037 { PCI_DEVICE_DATA(ADVA, TIMECARD, &ocp_adva_resource) }, 1038 + { PCI_DEVICE_DATA(ADVA, TIMECARD_X1, &ocp_adva_x1_resource) }, 1286 1039 { } 1287 1040 }; 1288 1041 MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id); 1289 1042 1290 1043 static DEFINE_MUTEX(ptp_ocp_lock); 1291 1044 static DEFINE_IDR(ptp_ocp_idr); 1292 - 1293 - struct ocp_selector { 1294 - const char *name; 1295 - int value; 1296 - u64 frequency; 1297 - }; 1298 1045 1299 1046 static const struct ocp_selector ptp_ocp_clock[] = { 1300 1047 { .name = "NONE", .value = 0 }, ··· 1380 1137 { } 1381 1138 }; 1382 1139 1383 - struct ocp_sma_op { 1384 - const struct ocp_selector *tbl[2]; 1385 - void (*init)(struct ptp_ocp *bp); 1386 - u32 (*get)(struct ptp_ocp *bp, int sma_nr); 1387 - int (*set_inputs)(struct ptp_ocp *bp, int sma_nr, u32 val); 1388 - int (*set_output)(struct ptp_ocp *bp, int sma_nr, u32 val); 1140 + static const struct ocp_selector ptp_ocp_adva_x1_sma_in[] = { 1141 + { .name = "PPS1", .value = 0x0001, .frequency = 1 }, 1142 + { .name = "TS1", .value = 0x0004, .frequency = 0 }, 1143 + { .name = "TS2", .value = 0x0008, .frequency = 0 }, 1144 + { .name = "TS3", .value = 0x0040, .frequency = 0 }, 1145 + { .name = "TS4", .value = 0x0080, .frequency = 0 }, 1146 + { .name = "FREQ1", .value = 0x0100, .frequency = 0 }, 1147 + { .name = "FREQ2", .value = 0x0200, .frequency = 0 }, 1148 + { .name = "FREQ3", .value = 0x0400, .frequency = 0 }, 1149 + { .name = "FREQ4", .value = 0x0800, .frequency = 0 }, 1150 + { .name = "None", .value = SMA_DISABLE, .frequency = 0 }, 1151 + { } 1152 + }; 1153 + 1154 + static const struct ocp_selector ptp_ocp_adva_x1_sma_out[] = { 1155 + { .name = "10Mhz", .value = 0x0000, .frequency = 10000000}, 1156 + { .name = "PHC", .value = 0x0001, .frequency = 1 }, 1157 + { .name = "MAC", .value = 0x0002, .frequency = 1 }, 1158 + { .name = "GNSS1", .value = 0x0004, .frequency = 1 }, 1159 + { .name = "GEN1", .value = 0x0040 }, 1160 + { .name = "GEN2", .value = 0x0080 }, 1161 + { .name = "GEN3", .value = 0x0100 }, 1162 + { .name = "GEN4", .value = 0x0200 }, 1163 + { .name = "GND", .value = 0x2000 }, 1164 + { .name = "VCC", .value = 0x4000 }, 1165 + { } 1389 1166 }; 1390 1167 1391 1168 static void ··· 2902 2639 .set_output = ptp_ocp_sma_adva_set_output, 2903 2640 }; 2904 2641 2642 + static const struct ocp_sma_op ocp_adva_x1_sma_op = { 2643 + .tbl = { ptp_ocp_adva_x1_sma_in, ptp_ocp_adva_x1_sma_out }, 2644 + .init = ptp_ocp_sma_fb_init, 2645 + .get = ptp_ocp_sma_fb_get, 2646 + .set_inputs = ptp_ocp_sma_adva_set_inputs, 2647 + .set_output = ptp_ocp_sma_adva_set_output, 2648 + }; 2649 + 2905 2650 static int 2906 2651 ptp_ocp_set_pins(struct ptp_ocp *bp) 2907 2652 { ··· 3158 2887 return ptp_ocp_init_clock(bp, r->extra); 3159 2888 } 3160 2889 3161 - /* ADVA specific board initializers; last "resource" registered. */ 2890 + /* ADVA board initializer; variant differences come from r->extra. */ 3162 2891 static int 3163 2892 ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r) 3164 2893 { 3165 - int err; 2894 + struct ptp_ocp_adva_info *info = r->extra; 3166 2895 u32 version; 2896 + int err; 3167 2897 3168 - bp->flash_start = 0xA00000; 3169 - bp->eeprom_map = fb_eeprom_map; 3170 - bp->sma_op = &ocp_adva_sma_op; 3171 - bp->signals_nr = 2; 3172 - bp->freq_in_nr = 2; 2898 + bp->flash_start = info->flash_start; 2899 + bp->eeprom_map = fb_eeprom_map; 2900 + bp->sma_op = info->sma_op; 2901 + bp->signals_nr = info->signals_nr; 2902 + bp->freq_in_nr = info->freq_in_nr; 3173 2903 3174 2904 version = ioread32(&bp->image->version); 3175 2905 /* if lower 16 bits are empty, this is the fw loader. */ ··· 3178 2906 version = version >> 16; 3179 2907 bp->fw_loader = true; 3180 2908 } 3181 - bp->fw_tag = 3; 2909 + bp->fw_tag = 3; 3182 2910 bp->fw_version = version & 0xffff; 3183 - bp->fw_cap = OCP_CAP_BASIC | OCP_CAP_SIGNAL | OCP_CAP_FREQ; 2911 + bp->fw_cap = OCP_CAP_BASIC | OCP_CAP_SIGNAL | OCP_CAP_FREQ; 3184 2912 3185 2913 ptp_ocp_tod_init(bp); 3186 2914 ptp_ocp_nmea_out_init(bp); 3187 2915 ptp_ocp_signal_init(bp); 3188 2916 3189 - err = ptp_ocp_attr_group_add(bp, adva_timecard_groups); 2917 + err = ptp_ocp_attr_group_add(bp, info->attr_groups); 3190 2918 if (err) 3191 2919 return err; 3192 2920 ··· 3195 2923 return err; 3196 2924 ptp_ocp_sma_init(bp); 3197 2925 3198 - return ptp_ocp_init_clock(bp, r->extra); 2926 + return ptp_ocp_init_clock(bp, &info->servo); 3199 2927 } 3200 2928 3201 2929 static ssize_t ··· 4251 3979 { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group }, 4252 3980 { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq0_group }, 4253 3981 { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq1_group }, 3982 + { }, 3983 + }; 3984 + 3985 + static struct attribute *adva_timecard_x1_attrs[] = { 3986 + &dev_attr_serialnum.attr, 3987 + &dev_attr_gnss_sync.attr, 3988 + &dev_attr_clock_source.attr, 3989 + &dev_attr_available_clock_sources.attr, 3990 + &dev_attr_sma1.attr, 3991 + &dev_attr_sma2.attr, 3992 + &dev_attr_sma3.attr, 3993 + &dev_attr_sma4.attr, 3994 + &dev_attr_available_sma_inputs.attr, 3995 + &dev_attr_available_sma_outputs.attr, 3996 + &dev_attr_clock_status_drift.attr, 3997 + &dev_attr_clock_status_offset.attr, 3998 + &dev_attr_ts_window_adjust.attr, 3999 + &dev_attr_utc_tai_offset.attr, 4000 + &dev_attr_tod_correction.attr, 4001 + NULL, 4002 + }; 4003 + 4004 + static const struct attribute_group adva_timecard_x1_group = { 4005 + .attrs = adva_timecard_x1_attrs, 4006 + }; 4007 + 4008 + static const struct ocp_attr_group adva_timecard_x1_groups[] = { 4009 + { .cap = OCP_CAP_BASIC, .group = &adva_timecard_x1_group }, 4010 + { .cap = OCP_CAP_BASIC, .group = &ptp_ocp_timecard_tty_group }, 4011 + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group }, 4012 + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group }, 4013 + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal2_group }, 4014 + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal3_group }, 4015 + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq0_group }, 4016 + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq1_group }, 4017 + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq2_group }, 4018 + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq3_group }, 4254 4019 { }, 4255 4020 }; 4256 4021