static EXT_ATTR_RO(freq, frequency, 2);
 static EXT_ATTR_RO(freq, frequency, 3);
 
+static ssize_t
+ptp_ocp_tty_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dev_ext_attribute *ea = to_ext_attr(attr);
+       struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+       return sysfs_emit(buf, "ttyS%d", bp->port[(uintptr_t)ea->var].line);
+}
+
+static umode_t
+ptp_ocp_timecard_tty_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+       struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+       struct ptp_ocp_serial_port *port;
+       struct device_attribute *dattr;
+       struct dev_ext_attribute *ea;
+
+       if (strncmp(attr->name, "tty", 3))
+               return attr->mode;
+
+       dattr = container_of(attr, struct device_attribute, attr);
+       ea = container_of(dattr, struct dev_ext_attribute, attr);
+       port = &bp->port[(uintptr_t)ea->var];
+       return port->line == -1 ? 0 : 0444;
+}
+
+#define EXT_TTY_ATTR_RO(_name, _val)                   \
+       struct dev_ext_attribute dev_attr_tty##_name =  \
+               { __ATTR(tty##_name, 0444, ptp_ocp_tty_show, NULL), (void *)_val }
+
+static EXT_TTY_ATTR_RO(GNSS, PORT_GNSS);
+static EXT_TTY_ATTR_RO(GNSS2, PORT_GNSS2);
+static EXT_TTY_ATTR_RO(MAC, PORT_MAC);
+static EXT_TTY_ATTR_RO(NMEA, PORT_NMEA);
+static struct attribute *ptp_ocp_timecard_tty_attrs[] = {
+       &dev_attr_ttyGNSS.attr.attr,
+       &dev_attr_ttyGNSS2.attr.attr,
+       &dev_attr_ttyMAC.attr.attr,
+       &dev_attr_ttyNMEA.attr.attr,
+       NULL,
+};
+
+static const struct attribute_group ptp_ocp_timecard_tty_group = {
+       .name = "tty",
+       .attrs = ptp_ocp_timecard_tty_attrs,
+       .is_visible = ptp_ocp_timecard_tty_is_visible,
+};
+
 static ssize_t
 serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 
 static const struct ocp_attr_group fb_timecard_groups[] = {
        { .cap = OCP_CAP_BASIC,     .group = &fb_timecard_group },
+       { .cap = OCP_CAP_BASIC,     .group = &ptp_ocp_timecard_tty_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal0_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal1_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal2_group },
 
 static const struct ocp_attr_group art_timecard_groups[] = {
        { .cap = OCP_CAP_BASIC,     .group = &art_timecard_group },
+       { .cap = OCP_CAP_BASIC,     .group = &ptp_ocp_timecard_tty_group },
        { },
 };
 
 
 static const struct ocp_attr_group adva_timecard_groups[] = {
        { .cap = OCP_CAP_BASIC,     .group = &adva_timecard_group },
+       { .cap = OCP_CAP_BASIC,     .group = &ptp_ocp_timecard_tty_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal0_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal1_group },
        { .cap = OCP_CAP_FREQ,      .group = &fb_timecard_freq0_group },
 {
        struct pps_device *pps;
        char buf[32];
-       int i;
-
-       for (i = 0; i < __PORT_COUNT; i++) {
-               if (bp->port[i].line != -1) {
-                       sprintf(buf, "ttyS%d", bp->port[i].line);
-                       ptp_ocp_link_child(bp, buf, ptp_ocp_tty_port_name(i));
-               }
-       }
 
        sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp));
        ptp_ocp_link_child(bp, buf, "ptp");
 {
        struct device *dev = &bp->dev;
 
-       sysfs_remove_link(&dev->kobj, "ttyGNSS");
-       sysfs_remove_link(&dev->kobj, "ttyGNSS2");
-       sysfs_remove_link(&dev->kobj, "ttyMAC");
        sysfs_remove_link(&dev->kobj, "ptp");
        sysfs_remove_link(&dev->kobj, "pps");
 }