Commit 86d6f77a authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ptp-ocxp-Oroli-ART-CARD'



Vadim Fedorenko says:

====================
ptp: ocp: add support for Orolia ART-CARD

Orolia company created alternative open source TimeCard. The hardware of
the card provides similar to OCP's card functions, that's why the support
is added to current driver.

The first patch in the series changes the way to store information about
serial ports and is more like preparation.

The patches 2 to 4 introduces actual hardware support.

The last patch removes fallback from devlink flashing interface to protect
against flashing wrong image. This became actual now as we have 2 different
boards supported and wrong image can ruin hardware easily.

v2:
  Address comments from Jonathan Lemon

v3:
  Fix issue reported by kernel test robot <lkp@intel.com>

v4:
  Fix clang build issue

v5:
  Fix warnings and per-patch build errors

v6:
  Fix more style issues
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4727bab4 c1fd463d
Loading
Loading
Loading
Loading
+515 −48
Original line number Diff line number Diff line
@@ -13,9 +13,11 @@
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/platform_data/i2c-xiic.h>
#include <linux/platform_data/i2c-ocores.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/spi/spi.h>
#include <linux/spi/xilinx_spi.h>
#include <linux/spi/altera.h>
#include <net/devlink.h>
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
@@ -28,6 +30,9 @@
#define PCI_VENDOR_ID_CELESTICA			0x18d4
#define PCI_DEVICE_ID_CELESTICA_TIMECARD	0x1008

#define PCI_VENDOR_ID_OROLIA			0x1ad7
#define PCI_DEVICE_ID_OROLIA_ARTCARD		0xa000

static struct class timecard_class = {
	.owner		= THIS_MODULE,
	.name		= "timecard",
@@ -203,6 +208,11 @@ struct frequency_reg {
	u32	ctrl;
	u32	status;
};

struct board_config_reg {
	u32 mro50_serial_activate;
};

#define FREQ_STATUS_VALID	BIT(31)
#define FREQ_STATUS_ERROR	BIT(30)
#define FREQ_STATUS_OVERRUN	BIT(29)
@@ -278,6 +288,11 @@ struct ptp_ocp_signal {
	bool		running;
};

struct ptp_ocp_serial_port {
	int line;
	int baud;
};

#define OCP_BOARD_ID_LEN		13
#define OCP_SERIAL_LEN			6

@@ -289,6 +304,7 @@ struct ptp_ocp {
	struct tod_reg __iomem	*tod;
	struct pps_reg __iomem	*pps_to_ext;
	struct pps_reg __iomem	*pps_to_clk;
	struct board_config_reg __iomem	*board_config;
	struct gpio_reg __iomem	*pps_select;
	struct gpio_reg __iomem	*sma_map1;
	struct gpio_reg __iomem	*sma_map2;
@@ -305,6 +321,7 @@ struct ptp_ocp {
	struct ptp_ocp_ext_src	*ts2;
	struct ptp_ocp_ext_src	*ts3;
	struct ptp_ocp_ext_src	*ts4;
	struct ocp_art_gpio_reg __iomem *art_sma;
	struct img_reg __iomem	*image;
	struct ptp_clock	*ptp;
	struct ptp_clock_info	ptp_info;
@@ -318,10 +335,10 @@ struct ptp_ocp {
	time64_t		gnss_lost;
	int			id;
	int			n_irqs;
	int			gnss_port;
	int			gnss2_port;
	int			mac_port;	/* miniature atomic clock */
	int			nmea_port;
	struct ptp_ocp_serial_port	gnss_port;
	struct ptp_ocp_serial_port	gnss2_port;
	struct ptp_ocp_serial_port	mac_port;   /* miniature atomic clock */
	struct ptp_ocp_serial_port	nmea_port;
	bool			fw_loader;
	u8			fw_tag;
	u16			fw_version;
@@ -365,8 +382,12 @@ static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen,
static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable);
static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr);

static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r);

static const struct ocp_attr_group fb_timecard_groups[];

static const struct ocp_attr_group art_timecard_groups[];

struct ptp_ocp_eeprom_map {
	u16	off;
	u16	len;
@@ -389,6 +410,12 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
	{ }
};

static struct ptp_ocp_eeprom_map art_eeprom_map[] = {
	{ EEPROM_ENTRY(0x200 + 0x43, board_id) },
	{ EEPROM_ENTRY(0x200 + 0x63, serial) },
	{ }
};

#define bp_assign_entry(bp, res, val) ({				\
	uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset;		\
	*(typeof(val) *)addr = val;					\
@@ -430,6 +457,13 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
 * 14: Signal Generator 4
 * 15: TS3
 * 16: TS4
 --
 * 8: Orolia TS1
 * 10: Orolia TS2
 * 11: Orolia TS0 (GNSS)
 * 12: Orolia PPS
 * 14: Orolia TS3
 * 15: Orolia TS4
 */

static struct ocp_resource ocp_fb_resource[] = {
@@ -596,14 +630,23 @@ static struct ocp_resource ocp_fb_resource[] = {
	{
		OCP_SERIAL_RESOURCE(gnss_port),
		.offset = 0x00160000 + 0x1000, .irq_vec = 3,
		.extra = &(struct ptp_ocp_serial_port) {
			.baud = 115200,
		},
	},
	{
		OCP_SERIAL_RESOURCE(gnss2_port),
		.offset = 0x00170000 + 0x1000, .irq_vec = 4,
		.extra = &(struct ptp_ocp_serial_port) {
			.baud = 115200,
		},
	},
	{
		OCP_SERIAL_RESOURCE(mac_port),
		.offset = 0x00180000 + 0x1000, .irq_vec = 5,
		.extra = &(struct ptp_ocp_serial_port) {
			.baud = 57600,
		},
	},
	{
		OCP_SERIAL_RESOURCE(nmea_port),
@@ -647,9 +690,141 @@ static struct ocp_resource ocp_fb_resource[] = {
	{ }
};

#define OCP_ART_CONFIG_SIZE		144
#define OCP_ART_TEMP_TABLE_SIZE		368

struct ocp_art_gpio_reg {
	struct {
		u32	gpio;
		u32	__pad[3];
	} map[4];
};

static struct ocp_resource ocp_art_resource[] = {
	{
		OCP_MEM_RESOURCE(reg),
		.offset = 0x01000000, .size = 0x10000,
	},
	{
		OCP_SERIAL_RESOURCE(gnss_port),
		.offset = 0x00160000 + 0x1000, .irq_vec = 3,
		.extra = &(struct ptp_ocp_serial_port) {
			.baud = 115200,
		},
	},
	{
		OCP_MEM_RESOURCE(art_sma),
		.offset = 0x003C0000, .size = 0x1000,
	},
	/* Timestamp associated with GNSS1 receiver PPS */
	{
		OCP_EXT_RESOURCE(ts0),
		.offset = 0x360000, .size = 0x20, .irq_vec = 12,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 0,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	{
		OCP_EXT_RESOURCE(ts1),
		.offset = 0x380000, .size = 0x20, .irq_vec = 8,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 1,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	{
		OCP_EXT_RESOURCE(ts2),
		.offset = 0x390000, .size = 0x20, .irq_vec = 10,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 2,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	{
		OCP_EXT_RESOURCE(ts3),
		.offset = 0x3A0000, .size = 0x20, .irq_vec = 14,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 3,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	{
		OCP_EXT_RESOURCE(ts4),
		.offset = 0x3B0000, .size = 0x20, .irq_vec = 15,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 4,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	/* Timestamp associated with Internal PPS of the card */
	{
		OCP_EXT_RESOURCE(pps),
		.offset = 0x00330000, .size = 0x20, .irq_vec = 11,
		.extra = &(struct ptp_ocp_ext_info) {
			.index = 5,
			.irq_fcn = ptp_ocp_ts_irq,
			.enable = ptp_ocp_ts_enable,
		},
	},
	{
		OCP_SPI_RESOURCE(spi_flash),
		.offset = 0x00310000, .size = 0x10000, .irq_vec = 9,
		.extra = &(struct ptp_ocp_flash_info) {
			.name = "spi_altera", .pci_offset = 0,
			.data_size = sizeof(struct altera_spi_platform_data),
			.data = &(struct altera_spi_platform_data) {
				.num_chipselect = 1,
				.num_devices = 1,
				.devices = &(struct spi_board_info) {
					.modalias = "spi-nor",
				},
			},
		},
	},
	{
		OCP_I2C_RESOURCE(i2c_ctrl),
		.offset = 0x350000, .size = 0x100, .irq_vec = 4,
		.extra = &(struct ptp_ocp_i2c_info) {
			.name = "ocores-i2c",
			.fixed_rate = 400000,
			.data_size = sizeof(struct ocores_i2c_platform_data),
			.data = &(struct ocores_i2c_platform_data) {
				.clock_khz = 125000,
				.bus_khz = 400,
				.num_devices = 1,
				.devices = &(struct i2c_board_info) {
					I2C_BOARD_INFO("24c08", 0x50),
				},
			},
		},
	},
	{
		OCP_SERIAL_RESOURCE(mac_port),
		.offset = 0x00190000, .irq_vec = 7,
		.extra = &(struct ptp_ocp_serial_port) {
			.baud = 9600,
		},
	},
	{
		OCP_MEM_RESOURCE(board_config),
		.offset = 0x210000, .size = 0x1000,
	},
	{
		.setup = ptp_ocp_art_board_init,
	},
	{ }
};

static const struct pci_device_id ptp_ocp_pcidev_id[] = {
	{ PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
	{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
	{ PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) },
	{ }
};
MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
@@ -714,6 +889,19 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
	{ }
};

static const struct ocp_selector ptp_ocp_art_sma_in[] = {
	{ .name = "PPS1",	.value = 0x0001 },
	{ .name = "10Mhz",	.value = 0x0008 },
	{ }
};

static const struct ocp_selector ptp_ocp_art_sma_out[] = {
	{ .name = "PHC",	.value = 0x0002 },
	{ .name = "GNSS",	.value = 0x0004 },
	{ .name = "10Mhz",	.value = 0x0010 },
	{ }
};

struct ocp_sma_op {
	const struct ocp_selector *tbl[2];
	void (*init)(struct ptp_ocp *bp);
@@ -1342,11 +1530,9 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
	hdr = (const struct ptp_ocp_firmware_header *)fw->data;
	if (memcmp(hdr->magic, OCP_FIRMWARE_MAGIC_HEADER, 4)) {
		devlink_flash_update_status_notify(devlink,
			"No firmware header found, flashing raw image",
			"No firmware header found, cancel firmware upgrade",
			NULL, 0, 0);
		offset = 0;
		length = fw->size;
		goto out;
		return -EINVAL;
	}

	if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor ||
@@ -1374,7 +1560,6 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
		return -EINVAL;
	}

out:
	*data = &fw->data[offset];
	*size = length;

@@ -1872,11 +2057,15 @@ ptp_ocp_serial_line(struct ptp_ocp *bp, struct ocp_resource *r)
static int
ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r)
{
	int port;
	struct ptp_ocp_serial_port *p = (struct ptp_ocp_serial_port *)r->extra;
	struct ptp_ocp_serial_port port = {};

	port.line = ptp_ocp_serial_line(bp, r);
	if (port.line < 0)
		return port.line;

	port = ptp_ocp_serial_line(bp, r);
	if (port < 0)
		return port;
	if (p)
		port.baud = p->baud;

	bp_assign_entry(bp, r, port);

@@ -2257,6 +2446,121 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
	return err;
}

static void
ptp_ocp_art_sma_init(struct ptp_ocp *bp)
{
	u32 reg;
	int i;

	/* defaults */
	bp->sma[0].mode = SMA_MODE_IN;
	bp->sma[1].mode = SMA_MODE_IN;
	bp->sma[2].mode = SMA_MODE_OUT;
	bp->sma[3].mode = SMA_MODE_OUT;

	bp->sma[0].default_fcn = 0x08;	/* IN: 10Mhz */
	bp->sma[1].default_fcn = 0x01;	/* IN: PPS1 */
	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */

	/* If no SMA map, the pin functions and directions are fixed. */
	if (!bp->art_sma) {
		for (i = 0; i < 4; i++) {
			bp->sma[i].fixed_fcn = true;
			bp->sma[i].fixed_dir = true;
		}
		return;
	}

	for (i = 0; i < 4; i++) {
		reg = ioread32(&bp->art_sma->map[i].gpio);

		switch (reg & 0xff) {
		case 0:
			bp->sma[i].fixed_fcn = true;
			bp->sma[i].fixed_dir = true;
			break;
		case 1:
		case 8:
			bp->sma[i].mode = SMA_MODE_IN;
			break;
		default:
			bp->sma[i].mode = SMA_MODE_OUT;
			break;
		}
	}
}

static u32
ptp_ocp_art_sma_get(struct ptp_ocp *bp, int sma_nr)
{
	if (bp->sma[sma_nr - 1].fixed_fcn)
		return bp->sma[sma_nr - 1].default_fcn;

	return ioread32(&bp->art_sma->map[sma_nr - 1].gpio) & 0xff;
}

/* note: store 0 is considered invalid. */
static int
ptp_ocp_art_sma_set(struct ptp_ocp *bp, int sma_nr, u32 val)
{
	unsigned long flags;
	u32 __iomem *gpio;
	int err = 0;
	u32 reg;

	val &= SMA_SELECT_MASK;
	if (hweight32(val) > 1)
		return -EINVAL;

	gpio = &bp->art_sma->map[sma_nr - 1].gpio;

	spin_lock_irqsave(&bp->lock, flags);
	reg = ioread32(gpio);
	if (((reg >> 16) & val) == 0) {
		err = -EOPNOTSUPP;
	} else {
		reg = (reg & 0xff00) | (val & 0xff);
		iowrite32(reg, gpio);
	}
	spin_unlock_irqrestore(&bp->lock, flags);

	return err;
}

static const struct ocp_sma_op ocp_art_sma_op = {
	.tbl		= { ptp_ocp_art_sma_in, ptp_ocp_art_sma_out },
	.init		= ptp_ocp_art_sma_init,
	.get		= ptp_ocp_art_sma_get,
	.set_inputs	= ptp_ocp_art_sma_set,
	.set_output	= ptp_ocp_art_sma_set,
};

/* ART specific board initializers; last "resource" registered. */
static int
ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
{
	int err;

	bp->flash_start = 0x1000000;
	bp->eeprom_map = art_eeprom_map;
	bp->fw_cap = OCP_CAP_BASIC;
	bp->fw_version = ioread32(&bp->reg->version);
	bp->fw_tag = 2;
	bp->sma_op = &ocp_art_sma_op;

	/* Enable MAC serial port during initialisation */
	iowrite32(1, &bp->board_config->mro50_serial_activate);

	ptp_ocp_sma_init(bp);

	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
	if (err)
		return err;

	return ptp_ocp_init_clock(bp);
}

static ssize_t
ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf,
		    int def_val)
@@ -3030,6 +3334,130 @@ DEVICE_FREQ_GROUP(freq2, 1);
DEVICE_FREQ_GROUP(freq3, 2);
DEVICE_FREQ_GROUP(freq4, 3);

static ssize_t
disciplining_config_read(struct file *filp, struct kobject *kobj,
			 struct bin_attribute *bin_attr, char *buf,
			 loff_t off, size_t count)
{
	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
	size_t size = OCP_ART_CONFIG_SIZE;
	struct nvmem_device *nvmem;
	ssize_t err;

	nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
	if (IS_ERR(nvmem))
		return PTR_ERR(nvmem);

	if (off > size) {
		err = 0;
		goto out;
	}

	if (off + count > size)
		count = size - off;

	// the configuration is in the very beginning of the EEPROM
	err = nvmem_device_read(nvmem, off, count, buf);
	if (err != count) {
		err = -EFAULT;
		goto out;
	}

out:
	ptp_ocp_nvmem_device_put(&nvmem);

	return err;
}

static ssize_t
disciplining_config_write(struct file *filp, struct kobject *kobj,
			  struct bin_attribute *bin_attr, char *buf,
			  loff_t off, size_t count)
{
	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
	struct nvmem_device *nvmem;
	ssize_t err;

	/* Allow write of the whole area only */
	if (off || count != OCP_ART_CONFIG_SIZE)
		return -EFAULT;

	nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
	if (IS_ERR(nvmem))
		return PTR_ERR(nvmem);

	err = nvmem_device_write(nvmem, 0x00, count, buf);
	if (err != count)
		err = -EFAULT;

	ptp_ocp_nvmem_device_put(&nvmem);

	return err;
}
static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);

static ssize_t
temperature_table_read(struct file *filp, struct kobject *kobj,
		       struct bin_attribute *bin_attr, char *buf,
		       loff_t off, size_t count)
{
	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
	size_t size = OCP_ART_TEMP_TABLE_SIZE;
	struct nvmem_device *nvmem;
	ssize_t err;

	nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
	if (IS_ERR(nvmem))
		return PTR_ERR(nvmem);

	if (off > size) {
		err = 0;
		goto out;
	}

	if (off + count > size)
		count = size - off;

	// the configuration is in the very beginning of the EEPROM
	err = nvmem_device_read(nvmem, 0x90 + off, count, buf);
	if (err != count) {
		err = -EFAULT;
		goto out;
	}

out:
	ptp_ocp_nvmem_device_put(&nvmem);

	return err;
}

static ssize_t
temperature_table_write(struct file *filp, struct kobject *kobj,
			struct bin_attribute *bin_attr, char *buf,
			loff_t off, size_t count)
{
	struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
	struct nvmem_device *nvmem;
	ssize_t err;

	/* Allow write of the whole area only */
	if (off || count != OCP_ART_TEMP_TABLE_SIZE)
		return -EFAULT;

	nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
	if (IS_ERR(nvmem))
		return PTR_ERR(nvmem);

	err = nvmem_device_write(nvmem, 0x90, count, buf);
	if (err != count)
		err = -EFAULT;

	ptp_ocp_nvmem_device_put(&nvmem);

	return err;
}
static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);

static struct attribute *fb_timecard_attrs[] = {
	&dev_attr_serialnum.attr,
	&dev_attr_gnss_sync.attr,
@@ -3049,9 +3477,11 @@ static struct attribute *fb_timecard_attrs[] = {
	&dev_attr_tod_correction.attr,
	NULL,
};

static const struct attribute_group fb_timecard_group = {
	.attrs = fb_timecard_attrs,
};

static const struct ocp_attr_group fb_timecard_groups[] = {
	{ .cap = OCP_CAP_BASIC,	    .group = &fb_timecard_group },
	{ .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal0_group },
@@ -3065,6 +3495,37 @@ static const struct ocp_attr_group fb_timecard_groups[] = {
	{ },
};

static struct attribute *art_timecard_attrs[] = {
	&dev_attr_serialnum.attr,
	&dev_attr_clock_source.attr,
	&dev_attr_available_clock_sources.attr,
	&dev_attr_utc_tai_offset.attr,
	&dev_attr_ts_window_adjust.attr,
	&dev_attr_sma1.attr,
	&dev_attr_sma2.attr,
	&dev_attr_sma3.attr,
	&dev_attr_sma4.attr,
	&dev_attr_available_sma_inputs.attr,
	&dev_attr_available_sma_outputs.attr,
	NULL,
};

static struct bin_attribute *bin_art_timecard_attrs[] = {
	&bin_attr_disciplining_config,
	&bin_attr_temperature_table,
	NULL,
};

static const struct attribute_group art_timecard_group = {
	.attrs = art_timecard_attrs,
	.bin_attrs = bin_art_timecard_attrs,
};

static const struct ocp_attr_group art_timecard_groups[] = {
	{ .cap = OCP_CAP_BASIC,	    .group = &art_timecard_group },
	{ },
};

static void
gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit,
	       const char *def)
@@ -3177,14 +3638,16 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
	bp = dev_get_drvdata(dev);

	seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp));
	if (bp->gnss_port != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1", bp->gnss_port);
	if (bp->gnss2_port != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2", bp->gnss2_port);
	if (bp->mac_port != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port);
	if (bp->nmea_port != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port);
	if (bp->gnss_port.line != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1",
			   bp->gnss_port.line);
	if (bp->gnss2_port.line != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2",
			   bp->gnss2_port.line);
	if (bp->mac_port.line != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port.line);
	if (bp->nmea_port.line != -1)
		seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port.line);

	memset(sma_val, 0xff, sizeof(sma_val));
	if (bp->sma_map1) {
@@ -3508,10 +3971,10 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)

	bp->ptp_info = ptp_ocp_clock_info;
	spin_lock_init(&bp->lock);
	bp->gnss_port = -1;
	bp->gnss2_port = -1;
	bp->mac_port = -1;
	bp->nmea_port = -1;
	bp->gnss_port.line = -1;
	bp->gnss2_port.line = -1;
	bp->mac_port.line = -1;
	bp->nmea_port.line = -1;
	bp->pdev = pdev;

	device_initialize(&bp->dev);
@@ -3569,20 +4032,20 @@ ptp_ocp_complete(struct ptp_ocp *bp)
	struct pps_device *pps;
	char buf[32];

	if (bp->gnss_port != -1) {
		sprintf(buf, "ttyS%d", bp->gnss_port);
	if (bp->gnss_port.line != -1) {
		sprintf(buf, "ttyS%d", bp->gnss_port.line);
		ptp_ocp_link_child(bp, buf, "ttyGNSS");
	}
	if (bp->gnss2_port != -1) {
		sprintf(buf, "ttyS%d", bp->gnss2_port);
	if (bp->gnss2_port.line != -1) {
		sprintf(buf, "ttyS%d", bp->gnss2_port.line);
		ptp_ocp_link_child(bp, buf, "ttyGNSS2");
	}
	if (bp->mac_port != -1) {
		sprintf(buf, "ttyS%d", bp->mac_port);
	if (bp->mac_port.line != -1) {
		sprintf(buf, "ttyS%d", bp->mac_port.line);
		ptp_ocp_link_child(bp, buf, "ttyMAC");
	}
	if (bp->nmea_port != -1) {
		sprintf(buf, "ttyS%d", bp->nmea_port);
	if (bp->nmea_port.line != -1) {
		sprintf(buf, "ttyS%d", bp->nmea_port.line);
		ptp_ocp_link_child(bp, buf, "ttyNMEA");
	}
	sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp));
@@ -3638,16 +4101,20 @@ ptp_ocp_info(struct ptp_ocp *bp)

	ptp_ocp_phc_info(bp);

	ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200);
	ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200);
	ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600);
	if (bp->nmea_out && bp->nmea_port != -1) {
		int baud = -1;
	ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port.line,
			    bp->gnss_port.baud);
	ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port.line,
			    bp->gnss2_port.baud);
	ptp_ocp_serial_info(dev, "MAC", bp->mac_port.line, bp->mac_port.baud);
	if (bp->nmea_out && bp->nmea_port.line != -1) {
		bp->nmea_port.baud = -1;

		reg = ioread32(&bp->nmea_out->uart_baud);
		if (reg < ARRAY_SIZE(nmea_baud))
			baud = nmea_baud[reg];
		ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud);
			bp->nmea_port.baud = nmea_baud[reg];

		ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port.line,
				    bp->nmea_port.baud);
	}
}

@@ -3688,14 +4155,14 @@ ptp_ocp_detach(struct ptp_ocp *bp)
	for (i = 0; i < 4; i++)
		if (bp->signal_out[i])
			ptp_ocp_unregister_ext(bp->signal_out[i]);
	if (bp->gnss_port != -1)
		serial8250_unregister_port(bp->gnss_port);
	if (bp->gnss2_port != -1)
		serial8250_unregister_port(bp->gnss2_port);
	if (bp->mac_port != -1)
		serial8250_unregister_port(bp->mac_port);
	if (bp->nmea_port != -1)
		serial8250_unregister_port(bp->nmea_port);
	if (bp->gnss_port.line != -1)
		serial8250_unregister_port(bp->gnss_port.line);
	if (bp->gnss2_port.line != -1)
		serial8250_unregister_port(bp->gnss2_port.line);
	if (bp->mac_port.line != -1)
		serial8250_unregister_port(bp->mac_port.line);
	if (bp->nmea_port.line != -1)
		serial8250_unregister_port(bp->nmea_port.line);
	platform_device_unregister(bp->spi_flash);
	platform_device_unregister(bp->i2c_ctrl);
	if (bp->i2c_clk)