diff options
Diffstat (limited to 'drivers/ptp/ptp_ocp.c')
-rw-r--r-- | drivers/ptp/ptp_ocp.c | 1688 |
1 files changed, 1386 insertions, 302 deletions
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 17ad5f0d13b2..c3d0fcf609e3 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -11,12 +11,14 @@ #include <linux/clkdev.h> #include <linux/clk-provider.h> #include <linux/platform_device.h> +#include <linux/platform_data/i2c-xiic.h> #include <linux/ptp_clock_kernel.h> #include <linux/spi/spi.h> #include <linux/spi/xilinx_spi.h> #include <net/devlink.h> #include <linux/i2c.h> #include <linux/mtd/mtd.h> +#include <linux/nvmem-consumer.h> #ifndef PCI_VENDOR_ID_FACEBOOK #define PCI_VENDOR_ID_FACEBOOK 0x1d9b @@ -52,6 +54,8 @@ struct ocp_reg { u32 servo_offset_i; u32 servo_drift_p; u32 servo_drift_i; + u32 status_offset; + u32 status_drift; }; #define OCP_CTRL_ENABLE BIT(0) @@ -88,9 +92,10 @@ struct tod_reg { #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) +#define TOD_STATUS_UTC_MASK 0xff +#define TOD_STATUS_UTC_VALID BIT(8) +#define TOD_STATUS_LEAP_ANNOUNCE BIT(12) +#define TOD_STATUS_LEAP_VALID BIT(16) struct ts_reg { u32 enable; @@ -174,6 +179,35 @@ struct dcf_slave_reg { #define DCF_S_CTRL_ENABLE BIT(0) +struct signal_reg { + u32 enable; + u32 status; + u32 polarity; + u32 version; + u32 __pad0[4]; + u32 cable_delay; + u32 __pad1[3]; + u32 intr; + u32 intr_mask; + u32 __pad2[2]; + u32 start_ns; + u32 start_sec; + u32 pulse_ns; + u32 pulse_sec; + u32 period_ns; + u32 period_sec; + u32 repeat_count; +}; + +struct frequency_reg { + u32 ctrl; + u32 status; +}; +#define FREQ_STATUS_VALID BIT(31) +#define FREQ_STATUS_ERROR BIT(30) +#define FREQ_STATUS_OVERRUN BIT(29) +#define FREQ_STATUS_MASK (BIT(24) - 1) + struct ptp_ocp_flash_info { const char *name; int pci_offset; @@ -201,6 +235,40 @@ struct ptp_ocp_ext_src { int irq_vec; }; +enum ptp_ocp_sma_mode { + SMA_MODE_IN, + SMA_MODE_OUT, +}; + +struct ptp_ocp_sma_connector { + enum ptp_ocp_sma_mode mode; + bool fixed_fcn; + bool fixed_dir; + bool disabled; +}; + +struct ocp_attr_group { + u64 cap; + const struct attribute_group *group; +}; + +#define OCP_CAP_BASIC BIT(0) +#define OCP_CAP_SIGNAL BIT(1) +#define OCP_CAP_FREQ BIT(2) + +struct ptp_ocp_signal { + ktime_t period; + ktime_t pulse; + ktime_t phase; + ktime_t start; + int duty; + bool polarity; + bool running; +}; + +#define OCP_BOARD_ID_LEN 13 +#define OCP_SERIAL_LEN 6 + struct ptp_ocp { struct pci_dev *pdev; struct device dev; @@ -210,16 +278,21 @@ struct ptp_ocp { struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_clk; struct gpio_reg __iomem *pps_select; - struct gpio_reg __iomem *sma; + struct gpio_reg __iomem *sma_map1; + struct gpio_reg __iomem *sma_map2; struct irig_master_reg __iomem *irig_out; struct irig_slave_reg __iomem *irig_in; struct dcf_master_reg __iomem *dcf_out; struct dcf_slave_reg __iomem *dcf_in; struct tod_reg __iomem *nmea_out; + struct frequency_reg __iomem *freq_in[4]; + struct ptp_ocp_ext_src *signal_out[4]; struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts1; struct ptp_ocp_ext_src *ts2; + struct ptp_ocp_ext_src *ts3; + struct ptp_ocp_ext_src *ts4; struct img_reg __iomem *image; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; @@ -227,6 +300,8 @@ struct ptp_ocp { struct platform_device *spi_flash; struct clk_hw *i2c_clk; struct timer_list watchdog; + const struct ocp_attr_group *attr_tbl; + const struct ptp_ocp_eeprom_map *eeprom_map; struct dentry *debug_root; time64_t gnss_lost; int id; @@ -235,12 +310,17 @@ struct ptp_ocp { int gnss2_port; int mac_port; /* miniature atomic clock */ int nmea_port; - u8 serial[6]; - bool has_serial; + u32 fw_version; + u8 board_id[OCP_BOARD_ID_LEN]; + u8 serial[OCP_SERIAL_LEN]; + bool has_eeprom_data; u32 pps_req_map; int flash_start; u32 utc_tai_offset; u32 ts_window_adjust; + u64 fw_cap; + struct ptp_ocp_signal signal[4]; + struct ptp_ocp_sma_connector sma[4]; }; #define OCP_REQ_TIMESTAMP BIT(0) @@ -263,7 +343,36 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); +static irqreturn_t ptp_ocp_signal_irq(int irq, void *priv); static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); +static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen, + struct ptp_perout_request *req); +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 const struct ocp_attr_group fb_timecard_groups[]; + +struct ptp_ocp_eeprom_map { + u16 off; + u16 len; + u32 bp_offset; + const void * const tag; +}; + +#define EEPROM_ENTRY(addr, member) \ + .off = addr, \ + .len = sizeof_field(struct ptp_ocp, member), \ + .bp_offset = offsetof(struct ptp_ocp, member) + +#define BP_MAP_ENTRY_ADDR(bp, map) ({ \ + (void *)((uintptr_t)(bp) + (map)->bp_offset); \ +}) + +static struct ptp_ocp_eeprom_map fb_eeprom_map[] = { + { EEPROM_ENTRY(0x43, board_id) }, + { EEPROM_ENTRY(0x00, serial), .tag = "mac" }, + { } +}; #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ @@ -289,10 +398,10 @@ static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext /* This is the MSI vector mapping used. - * 0: TS3 (and PPS) + * 0: PPS (TS5) * 1: TS0 * 2: TS1 - * 3: GNSS + * 3: GNSS1 * 4: GNSS2 * 5: MAC * 6: TS2 @@ -300,6 +409,12 @@ static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); * 8: HWICAP (notused) * 9: SPI Flash * 10: NMEA + * 11: Signal Generator 1 + * 12: Signal Generator 2 + * 13: Signal Generator 3 + * 14: Signal Generator 4 + * 15: TS3 + * 16: TS4 */ static struct ocp_resource ocp_fb_resource[] = { @@ -335,15 +450,70 @@ static struct ocp_resource ocp_fb_resource[] = { }, }, { + OCP_EXT_RESOURCE(ts3), + .offset = 0x01110000, .size = 0x10000, .irq_vec = 15, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + { + OCP_EXT_RESOURCE(ts4), + .offset = 0x01120000, .size = 0x10000, .irq_vec = 16, + .extra = &(struct ptp_ocp_ext_info) { + .index = 4, + .irq_fcn = ptp_ocp_ts_irq, + .enable = ptp_ocp_ts_enable, + }, + }, + /* Timestamp for PHC and/or PPS generator */ + { OCP_EXT_RESOURCE(pps), .offset = 0x010C0000, .size = 0x10000, .irq_vec = 0, .extra = &(struct ptp_ocp_ext_info) { - .index = 3, + .index = 5, .irq_fcn = ptp_ocp_ts_irq, .enable = ptp_ocp_ts_enable, }, }, { + OCP_EXT_RESOURCE(signal_out[0]), + .offset = 0x010D0000, .size = 0x10000, .irq_vec = 11, + .extra = &(struct ptp_ocp_ext_info) { + .index = 1, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[1]), + .offset = 0x010E0000, .size = 0x10000, .irq_vec = 12, + .extra = &(struct ptp_ocp_ext_info) { + .index = 2, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[2]), + .offset = 0x010F0000, .size = 0x10000, .irq_vec = 13, + .extra = &(struct ptp_ocp_ext_info) { + .index = 3, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { + OCP_EXT_RESOURCE(signal_out[3]), + .offset = 0x01100000, .size = 0x10000, .irq_vec = 14, + .extra = &(struct ptp_ocp_ext_info) { + .index = 4, + .irq_fcn = ptp_ocp_signal_irq, + .enable = ptp_ocp_signal_enable, + }, + }, + { OCP_MEM_RESOURCE(pps_to_ext), .offset = 0x01030000, .size = 0x10000, }, @@ -384,15 +554,28 @@ static struct ocp_resource ocp_fb_resource[] = { .offset = 0x00130000, .size = 0x1000, }, { - OCP_MEM_RESOURCE(sma), + OCP_MEM_RESOURCE(sma_map1), .offset = 0x00140000, .size = 0x1000, }, { + OCP_MEM_RESOURCE(sma_map2), + .offset = 0x00220000, .size = 0x1000, + }, + { OCP_I2C_RESOURCE(i2c_ctrl), .offset = 0x00150000, .size = 0x10000, .irq_vec = 7, .extra = &(struct ptp_ocp_i2c_info) { .name = "xiic-i2c", .fixed_rate = 50000000, + .data_size = sizeof(struct xiic_i2c_platform_data), + .data = &(struct xiic_i2c_platform_data) { + .num_devices = 2, + .devices = (struct i2c_board_info[]) { + { I2C_BOARD_INFO("24c02", 0x50) }, + { I2C_BOARD_INFO("24mac402", 0x58), + .platform_data = "mac" }, + }, + }, }, }, { @@ -428,6 +611,22 @@ static struct ocp_resource ocp_fb_resource[] = { }, }, { + OCP_MEM_RESOURCE(freq_in[0]), + .offset = 0x01200000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[1]), + .offset = 0x01210000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[2]), + .offset = 0x01220000, .size = 0x10000, + }, + { + OCP_MEM_RESOURCE(freq_in[3]), + .offset = 0x01230000, .size = 0x10000, + }, + { .setup = ptp_ocp_fb_board_init, }, { } @@ -460,25 +659,42 @@ static struct ocp_selector ptp_ocp_clock[] = { { } }; +#define SMA_ENABLE BIT(15) +#define SMA_SELECT_MASK ((1U << 15) - 1) +#define SMA_DISABLE 0x10000 + static struct ocp_selector ptp_ocp_sma_in[] = { - { .name = "10Mhz", .value = 0x00 }, - { .name = "PPS1", .value = 0x01 }, - { .name = "PPS2", .value = 0x02 }, - { .name = "TS1", .value = 0x04 }, - { .name = "TS2", .value = 0x08 }, - { .name = "IRIG", .value = 0x10 }, - { .name = "DCF", .value = 0x20 }, + { .name = "10Mhz", .value = 0x0000 }, + { .name = "PPS1", .value = 0x0001 }, + { .name = "PPS2", .value = 0x0002 }, + { .name = "TS1", .value = 0x0004 }, + { .name = "TS2", .value = 0x0008 }, + { .name = "IRIG", .value = 0x0010 }, + { .name = "DCF", .value = 0x0020 }, + { .name = "TS3", .value = 0x0040 }, + { .name = "TS4", .value = 0x0080 }, + { .name = "FREQ1", .value = 0x0100 }, + { .name = "FREQ2", .value = 0x0200 }, + { .name = "FREQ3", .value = 0x0400 }, + { .name = "FREQ4", .value = 0x0800 }, + { .name = "None", .value = SMA_DISABLE }, { } }; static struct ocp_selector ptp_ocp_sma_out[] = { - { .name = "10Mhz", .value = 0x00 }, - { .name = "PHC", .value = 0x01 }, - { .name = "MAC", .value = 0x02 }, - { .name = "GNSS", .value = 0x04 }, - { .name = "GNSS2", .value = 0x08 }, - { .name = "IRIG", .value = 0x10 }, - { .name = "DCF", .value = 0x20 }, + { .name = "10Mhz", .value = 0x0000 }, + { .name = "PHC", .value = 0x0001 }, + { .name = "MAC", .value = 0x0002 }, + { .name = "GNSS1", .value = 0x0004 }, + { .name = "GNSS2", .value = 0x0008 }, + { .name = "IRIG", .value = 0x0010 }, + { .name = "DCF", .value = 0x0020 }, + { .name = "GEN1", .value = 0x0040 }, + { .name = "GEN2", .value = 0x0080 }, + { .name = "GEN3", .value = 0x0100 }, + { .name = "GEN4", .value = 0x0200 }, + { .name = "GND", .value = 0x2000 }, + { .name = "VCC", .value = 0x4000 }, { } }; @@ -700,6 +916,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ext = bp->ts2; break; case 3: + ext = bp->ts3; + break; + case 4: + ext = bp->ts4; + break; + case 5: ext = bp->pps; break; } @@ -709,13 +931,27 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ext = bp->pps; break; case PTP_CLK_REQ_PEROUT: - if (on && - (rq->perout.period.sec != 1 || rq->perout.period.nsec != 0)) - return -EINVAL; - /* This is a request for 1PPS on an output SMA. - * Allow, but assume manual configuration. - */ - return 0; + switch (rq->perout.index) { + case 0: + /* This is a request for 1PPS on an output SMA. + * Allow, but assume manual configuration. + */ + if (on && (rq->perout.period.sec != 1 || + rq->perout.period.nsec != 0)) + return -EINVAL; + return 0; + case 1: + case 2: + case 3: + case 4: + req = rq->perout.index - 1; + ext = bp->signal_out[req]; + err = ptp_ocp_signal_from_perout(bp, req, &rq->perout); + if (err) + return err; + break; + } + break; default: return -EOPNOTSUPP; } @@ -727,6 +963,36 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, return err; } +static int +ptp_ocp_verify(struct ptp_clock_info *ptp_info, unsigned pin, + enum ptp_pin_function func, unsigned chan) +{ + struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); + char buf[16]; + + switch (func) { + case PTP_PF_NONE: + snprintf(buf, sizeof(buf), "IN: None"); + break; + case PTP_PF_EXTTS: + /* Allow timestamps, but require sysfs configuration. */ + return 0; + case PTP_PF_PEROUT: + /* channel 0 is 1PPS from PHC. + * channels 1..4 are the frequency generators. + */ + if (chan) + snprintf(buf, sizeof(buf), "OUT: GEN%d", chan); + else + snprintf(buf, sizeof(buf), "OUT: PHC"); + break; + default: + return -EOPNOTSUPP; + } + + return ptp_ocp_sma_store(bp, buf, pin + 1); +} + static const struct ptp_clock_info ptp_ocp_clock_info = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, @@ -737,9 +1003,10 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { .adjfine = ptp_ocp_null_adjfine, .adjphase = ptp_ocp_null_adjphase, .enable = ptp_ocp_enable, + .verify = ptp_ocp_verify, .pps = true, - .n_ext_ts = 4, - .n_per_out = 1, + .n_ext_ts = 6, + .n_per_out = 5, }; static void @@ -760,11 +1027,30 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) } static void +ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + bp->utc_tai_offset = val; + + if (bp->irig_out) + iowrite32(val, &bp->irig_out->adj_sec); + if (bp->dcf_out) + iowrite32(val, &bp->dcf_out->adj_sec); + if (bp->nmea_out) + iowrite32(val, &bp->nmea_out->adj_sec); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void ptp_ocp_watchdog(struct timer_list *t) { struct ptp_ocp *bp = from_timer(bp, t, watchdog); unsigned long flags; - u32 status; + u32 status, utc_offset; status = ioread32(&bp->pps_to_clk->status); @@ -781,6 +1067,17 @@ ptp_ocp_watchdog(struct timer_list *t) bp->gnss_lost = 0; } + /* if GNSS provides correct data we can rely on + * it to get leap second information + */ + if (bp->tod) { + status = ioread32(&bp->tod->utc_status); + utc_offset = status & TOD_STATUS_UTC_MASK; + if (status & TOD_STATUS_UTC_VALID && + utc_offset != bp->utc_tai_offset) + ptp_ocp_utc_distribute(bp, utc_offset); + } + mod_timer(&bp->watchdog, jiffies + HZ); } @@ -850,25 +1147,6 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) } static void -ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - - bp->utc_tai_offset = val; - - if (bp->irig_out) - iowrite32(val, &bp->irig_out->adj_sec); - if (bp->dcf_out) - iowrite32(val, &bp->dcf_out->adj_sec); - if (bp->nmea_out) - iowrite32(val, &bp->nmea_out->adj_sec); - - spin_unlock_irqrestore(&bp->lock, flags); -} - -static void ptp_ocp_tod_init(struct ptp_ocp *bp) { u32 ctrl, reg; @@ -883,119 +1161,110 @@ ptp_ocp_tod_init(struct ptp_ocp *bp) ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK); } -static void -ptp_ocp_tod_info(struct ptp_ocp *bp) +static const char * +ptp_ocp_tod_proto_name(const int idx) { static const char * const proto_name[] = { "NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none", "UBX", "UBX_UTC", "UBX_LS", "UBX_none" }; + return proto_name[idx]; +} + +static const char * +ptp_ocp_tod_gnss_name(int idx) +{ static const char * const gnss_name[] = { "ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU", + "Unknown" }; - 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); - 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" : ""); + if (idx >= ARRAY_SIZE(gnss_name)) + idx = ARRAY_SIZE(gnss_name) - 1; + return gnss_name[idx]; +} - 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]); +struct ptp_ocp_nvmem_match_info { + struct ptp_ocp *bp; + const void * const tag; +}; - reg = ioread32(&bp->tod->status); - dev_info(&bp->pdev->dev, "status: %x\n", reg); +static int +ptp_ocp_nvmem_match(struct device *dev, const void *data) +{ + const struct ptp_ocp_nvmem_match_info *info = data; - reg = ioread32(&bp->tod->adj_sec); - dev_info(&bp->pdev->dev, "correction: %d\n", reg); + dev = dev->parent; + if (!i2c_verify_client(dev) || info->tag != dev->platform_data) + return 0; - 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); + while ((dev = dev->parent)) + if (dev->driver && !strcmp(dev->driver->name, KBUILD_MODNAME)) + return info->bp == dev_get_drvdata(dev); + return 0; } -static int -ptp_ocp_firstchild(struct device *dev, void *data) +static inline struct nvmem_device * +ptp_ocp_nvmem_device_get(struct ptp_ocp *bp, const void * const tag) { - return 1; + struct ptp_ocp_nvmem_match_info info = { .bp = bp, .tag = tag }; + + return nvmem_device_find(&info, ptp_ocp_nvmem_match); } -static int -ptp_ocp_read_i2c(struct i2c_adapter *adap, u8 addr, u8 reg, u8 sz, u8 *data) +static inline void +ptp_ocp_nvmem_device_put(struct nvmem_device **nvmemp) { - struct i2c_msg msgs[2] = { - { - .addr = addr, - .len = 1, - .buf = ®, - }, - { - .addr = addr, - .flags = I2C_M_RD, - .len = 2, - .buf = data, - }, - }; - int err; - u8 len; - - /* xiic-i2c for some stupid reason only does 2 byte reads. */ - while (sz) { - len = min_t(u8, sz, 2); - msgs[1].len = len; - err = i2c_transfer(adap, msgs, 2); - if (err != msgs[1].len) - return err; - msgs[1].buf += len; - reg += len; - sz -= len; + if (*nvmemp != NULL) { + nvmem_device_put(*nvmemp); + *nvmemp = NULL; } - return 0; } static void -ptp_ocp_get_serial_number(struct ptp_ocp *bp) +ptp_ocp_read_eeprom(struct ptp_ocp *bp) { - struct i2c_adapter *adap; - struct device *dev; - int err; + const struct ptp_ocp_eeprom_map *map; + struct nvmem_device *nvmem; + const void *tag; + int ret; if (!bp->i2c_ctrl) return; - dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); - if (!dev) { - dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); - return; - } - - adap = i2c_verify_adapter(dev); - if (!adap) { - dev_err(&bp->pdev->dev, "device '%s' isn't an I2C adapter\n", - dev_name(dev)); - goto out; - } + tag = NULL; + nvmem = NULL; - err = ptp_ocp_read_i2c(adap, 0x58, 0x9A, 6, bp->serial); - if (err) { - dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", err); - goto out; + for (map = bp->eeprom_map; map->len; map++) { + if (map->tag != tag) { + tag = map->tag; + ptp_ocp_nvmem_device_put(&nvmem); + } + if (!nvmem) { + nvmem = ptp_ocp_nvmem_device_get(bp, tag); + if (!nvmem) + goto out; + } + ret = nvmem_device_read(nvmem, map->off, map->len, + BP_MAP_ENTRY_ADDR(bp, map)); + if (ret != map->len) + goto read_fail; } - bp->has_serial = true; + bp->has_eeprom_data = true; out: - put_device(dev); + ptp_ocp_nvmem_device_put(&nvmem); + return; + +read_fail: + dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", ret); + goto out; +} + +static int +ptp_ocp_firstchild(struct device *dev, void *data) +{ + return 1; } static struct device * @@ -1096,34 +1365,33 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; - if (bp->image) { - u32 ver = ioread32(&bp->image->version); - - if (ver & 0xffff) { - sprintf(buf, "%d", ver); - err = devlink_info_version_running_put(req, - "fw", - buf); - } else { - sprintf(buf, "%d", ver >> 16); - err = devlink_info_version_running_put(req, - "loader", - buf); - } - if (err) - return err; + if (bp->fw_version & 0xffff) { + sprintf(buf, "%d", bp->fw_version); + err = devlink_info_version_running_put(req, "fw", buf); + } else { + sprintf(buf, "%d", bp->fw_version >> 16); + err = devlink_info_version_running_put(req, "loader", buf); } + if (err) + return err; - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); - - if (bp->has_serial) { - sprintf(buf, "%pM", bp->serial); - err = devlink_info_serial_number_put(req, buf); - if (err) - return err; + if (!bp->has_eeprom_data) { + ptp_ocp_read_eeprom(bp); + if (!bp->has_eeprom_data) + return 0; } + sprintf(buf, "%pM", bp->serial); + err = devlink_info_serial_number_put(req, buf); + if (err) + return err; + + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, + bp->board_id); + if (err) + return err; + return 0; } @@ -1240,6 +1508,137 @@ ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) return 0; } +/* The expectation is that this is triggered only on error. */ +static irqreturn_t +ptp_ocp_signal_irq(int irq, void *priv) +{ + struct ptp_ocp_ext_src *ext = priv; + struct signal_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + u32 enable, status; + int gen; + + gen = ext->info->index - 1; + + enable = ioread32(®->enable); + status = ioread32(®->status); + + /* disable generator on error */ + if (status || !enable) { + iowrite32(0, ®->intr_mask); + iowrite32(0, ®->enable); + bp->signal[gen].running = false; + } + + iowrite32(0, ®->intr); /* ack interrupt */ + + return IRQ_HANDLED; +} + +static int +ptp_ocp_signal_set(struct ptp_ocp *bp, int gen, struct ptp_ocp_signal *s) +{ + struct ptp_system_timestamp sts; + struct timespec64 ts; + ktime_t start_ns; + int err; + + if (!s->period) + return 0; + + if (!s->pulse) + s->pulse = ktime_divns(s->period * s->duty, 100); + + err = ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts); + if (err) + return err; + + start_ns = ktime_set(ts.tv_sec, ts.tv_nsec) + NSEC_PER_MSEC; + if (!s->start) { + /* roundup() does not work on 32-bit systems */ + s->start = DIV_ROUND_UP_ULL(start_ns, s->period); + s->start = ktime_add(s->start, s->phase); + } + + if (s->duty < 1 || s->duty > 99) + return -EINVAL; + + if (s->pulse < 1 || s->pulse > s->period) + return -EINVAL; + + if (s->start < start_ns) + return -EINVAL; + + bp->signal[gen] = *s; + + return 0; +} + +static int +ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen, + struct ptp_perout_request *req) +{ + struct ptp_ocp_signal s = { }; + + s.polarity = bp->signal[gen].polarity; + s.period = ktime_set(req->period.sec, req->period.nsec); + if (!s.period) + return 0; + + if (req->flags & PTP_PEROUT_DUTY_CYCLE) { + s.pulse = ktime_set(req->on.sec, req->on.nsec); + s.duty = ktime_divns(s.pulse * 100, s.period); + } + + if (req->flags & PTP_PEROUT_PHASE) + s.phase = ktime_set(req->phase.sec, req->phase.nsec); + else + s.start = ktime_set(req->start.sec, req->start.nsec); + + return ptp_ocp_signal_set(bp, gen, &s); +} + +static int +ptp_ocp_signal_enable(void *priv, u32 req, bool enable) +{ + struct ptp_ocp_ext_src *ext = priv; + struct signal_reg __iomem *reg = ext->mem; + struct ptp_ocp *bp = ext->bp; + struct timespec64 ts; + int gen; + + gen = ext->info->index - 1; + + iowrite32(0, ®->intr_mask); + iowrite32(0, ®->enable); + bp->signal[gen].running = false; + if (!enable) + return 0; + + ts = ktime_to_timespec64(bp->signal[gen].start); + iowrite32(ts.tv_sec, ®->start_sec); + iowrite32(ts.tv_nsec, ®->start_ns); + + ts = ktime_to_timespec64(bp->signal[gen].period); + iowrite32(ts.tv_sec, ®->period_sec); + iowrite32(ts.tv_nsec, ®->period_ns); + + ts = ktime_to_timespec64(bp->signal[gen].pulse); + iowrite32(ts.tv_sec, ®->pulse_sec); + iowrite32(ts.tv_nsec, ®->pulse_ns); + + iowrite32(bp->signal[gen].polarity, ®->polarity); + iowrite32(0, ®->repeat_count); + + iowrite32(0, ®->intr); /* clear interrupt state */ + iowrite32(1, ®->intr_mask); /* enable interrupt */ + iowrite32(3, ®->enable); /* valid & enable */ + + bp->signal[gen].running = true; + + return 0; +} + static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv) { @@ -1367,7 +1766,7 @@ ptp_ocp_serial_line(struct ptp_ocp *bp, struct ocp_resource *r) uart.port.mapbase = pci_resource_start(pdev, 0) + r->offset; uart.port.irq = pci_irq_vector(pdev, r->irq_vec); uart.port.uartclk = 50000000; - uart.port.flags = UPF_FIXED_TYPE | UPF_IOREMAP; + uart.port.flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_NO_THRE_TEST; uart.port.type = PORT_16550A; return serial8250_register_8250_port(&uart); @@ -1412,14 +1811,115 @@ ptp_ocp_nmea_out_init(struct ptp_ocp *bp) iowrite32(1, &bp->nmea_out->ctrl); /* enable */ } +static void +_ptp_ocp_signal_init(struct ptp_ocp_signal *s, struct signal_reg __iomem *reg) +{ + u32 val; + + iowrite32(0, ®->enable); /* disable */ + + val = ioread32(®->polarity); + s->polarity = val ? true : false; + s->duty = 50; +} + +static void +ptp_ocp_signal_init(struct ptp_ocp *bp) +{ + int i; + + for (i = 0; i < 4; i++) + if (bp->signal_out[i]) + _ptp_ocp_signal_init(&bp->signal[i], + bp->signal_out[i]->mem); +} + +static void +ptp_ocp_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; + + /* If no SMA1 map, the pin functions and directions are fixed. */ + if (!bp->sma_map1) { + for (i = 0; i < 4; i++) { + bp->sma[i].fixed_fcn = true; + bp->sma[i].fixed_dir = true; + } + return; + } + + /* If SMA2 GPIO output map is all 1, it is not present. + * This indicates the firmware has fixed direction SMA pins. + */ + reg = ioread32(&bp->sma_map2->gpio2); + if (reg == 0xffffffff) { + for (i = 0; i < 4; i++) + bp->sma[i].fixed_dir = true; + } else { + reg = ioread32(&bp->sma_map1->gpio1); + bp->sma[0].mode = reg & BIT(15) ? SMA_MODE_IN : SMA_MODE_OUT; + bp->sma[1].mode = reg & BIT(31) ? SMA_MODE_IN : SMA_MODE_OUT; + + reg = ioread32(&bp->sma_map1->gpio2); + bp->sma[2].mode = reg & BIT(15) ? SMA_MODE_OUT : SMA_MODE_IN; + bp->sma[3].mode = reg & BIT(31) ? SMA_MODE_OUT : SMA_MODE_IN; + } +} + +static int +ptp_ocp_fb_set_pins(struct ptp_ocp *bp) +{ + struct ptp_pin_desc *config; + int i; + + config = kzalloc(sizeof(*config) * 4, GFP_KERNEL); + if (!config) + return -ENOMEM; + + for (i = 0; i < 4; i++) { + sprintf(config[i].name, "sma%d", i + 1); + config[i].index = i; + } + + bp->ptp_info.n_pins = 4; + bp->ptp_info.pin_config = config; + + return 0; +} + /* FB specific board initializers; last "resource" registered. */ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) { + int ver, err; + bp->flash_start = 1024 * 4096; + bp->eeprom_map = fb_eeprom_map; + bp->fw_version = ioread32(&bp->image->version); + bp->attr_tbl = fb_timecard_groups; + bp->fw_cap = OCP_CAP_BASIC; + + ver = bp->fw_version & 0xffff; + if (ver >= 19) + bp->fw_cap |= OCP_CAP_SIGNAL; + if (ver >= 20) + bp->fw_cap |= OCP_CAP_FREQ; ptp_ocp_tod_init(bp); ptp_ocp_nmea_out_init(bp); + ptp_ocp_sma_init(bp); + ptp_ocp_signal_init(bp); + + err = ptp_ocp_fb_set_pins(bp); + if (err) + return err; return ptp_ocp_init_clock(bp); } @@ -1521,38 +2021,8 @@ __handle_signal_inputs(struct ptp_ocp *bp, u32 val) * ANT4 == sma4 (out) */ -enum ptp_ocp_sma_mode { - SMA_MODE_IN, - SMA_MODE_OUT, -}; - -static struct ptp_ocp_sma_connector { - enum ptp_ocp_sma_mode mode; - bool fixed_mode; - u16 default_out_idx; -} ptp_ocp_sma_map[4] = { - { - .mode = SMA_MODE_IN, - .fixed_mode = true, - }, - { - .mode = SMA_MODE_IN, - .fixed_mode = true, - }, - { - .mode = SMA_MODE_OUT, - .fixed_mode = true, - .default_out_idx = 0, /* 10Mhz */ - }, - { - .mode = SMA_MODE_OUT, - .fixed_mode = true, - .default_out_idx = 1, /* PHC */ - }, -}; - static ssize_t -ptp_ocp_show_output(u32 val, char *buf, int default_idx) +ptp_ocp_show_output(u32 val, char *buf, int def_val) { const char *name; ssize_t count; @@ -1560,13 +2030,13 @@ ptp_ocp_show_output(u32 val, char *buf, int default_idx) count = sysfs_emit(buf, "OUT: "); name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val); if (!name) - name = ptp_ocp_sma_out[default_idx].name; + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, def_val); count += sysfs_emit_at(buf, count, "%s\n", name); return count; } static ssize_t -ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) +ptp_ocp_show_inputs(u32 val, char *buf, int def_val) { const char *name; ssize_t count; @@ -1579,8 +2049,10 @@ ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in) count += sysfs_emit_at(buf, count, "%s ", name); } } - if (!val && zero_in) - count += sysfs_emit_at(buf, count, "%s ", zero_in); + if (!val && def_val >= 0) { + name = ptp_ocp_select_name_from_val(ptp_ocp_sma_in, def_val); + count += sysfs_emit_at(buf, count, "%s ", name); + } if (count) count--; count += sysfs_emit_at(buf, count, "\n"); @@ -1605,7 +2077,7 @@ sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode) idx = 0; dir = *mode == SMA_MODE_IN ? 0 : 1; - if (!strcasecmp("IN:", argv[idx])) { + if (!strcasecmp("IN:", argv[0])) { dir = 0; idx++; } @@ -1626,102 +2098,126 @@ out: return ret; } +static u32 +ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr, enum ptp_ocp_sma_mode mode) +{ + u32 __iomem *gpio; + u32 shift; + + if (bp->sma[sma_nr - 1].fixed_fcn) + return (sma_nr - 1) & 1; + + if (mode == SMA_MODE_IN) + gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1; + else + gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2; + shift = sma_nr & 1 ? 0 : 16; + + return (ioread32(gpio) >> shift) & 0xffff; +} + static ssize_t -ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf, - const char *zero_in) +ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, char *buf, + int default_in_val, int default_out_val) { - struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1]; + u32 val; + + val = ptp_ocp_sma_get(bp, sma_nr, sma->mode) & SMA_SELECT_MASK; - if (sma->mode == SMA_MODE_IN) - return ptp_ocp_show_inputs(val, buf, zero_in); + if (sma->mode == SMA_MODE_IN) { + if (sma->disabled) + val = SMA_DISABLE; + return ptp_ocp_show_inputs(val, buf, default_in_val); + } - return ptp_ocp_show_output(val, buf, sma->default_out_idx); + return ptp_ocp_show_output(val, buf, default_out_val); } static ssize_t sma1_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = ioread32(&bp->sma->gpio1) & 0x3f; - return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name); + return ptp_ocp_sma_show(bp, 1, buf, 0, 1); } static ssize_t sma2_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f; - return ptp_ocp_sma_show(bp, 2, val, buf, NULL); + return ptp_ocp_sma_show(bp, 2, buf, -1, 1); } static ssize_t sma3_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = ioread32(&bp->sma->gpio2) & 0x3f; - return ptp_ocp_sma_show(bp, 3, val, buf, NULL); + return ptp_ocp_sma_show(bp, 3, buf, -1, 0); } static ssize_t sma4_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - u32 val; - val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f; - return ptp_ocp_sma_show(bp, 4, val, buf, NULL); + return ptp_ocp_sma_show(bp, 4, buf, -1, 1); } static void -ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift) +ptp_ocp_sma_store_output(struct ptp_ocp *bp, int sma_nr, u32 val) { + u32 reg, mask, shift; unsigned long flags; - u32 gpio, mask; + u32 __iomem *gpio; + + gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2; + shift = sma_nr & 1 ? 0 : 16; mask = 0xffff << (16 - shift); spin_lock_irqsave(&bp->lock, flags); - gpio = ioread32(&bp->sma->gpio2); - gpio = (gpio & mask) | (val << shift); + reg = ioread32(gpio); + reg = (reg & mask) | (val << shift); - __handle_signal_outputs(bp, gpio); + __handle_signal_outputs(bp, reg); - iowrite32(gpio, &bp->sma->gpio2); + iowrite32(reg, gpio); spin_unlock_irqrestore(&bp->lock, flags); } static void -ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift) +ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, int sma_nr, u32 val) { + u32 reg, mask, shift; unsigned long flags; - u32 gpio, mask; + u32 __iomem *gpio; + + gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1; + shift = sma_nr & 1 ? 0 : 16; mask = 0xffff << (16 - shift); spin_lock_irqsave(&bp->lock, flags); - gpio = ioread32(&bp->sma->gpio1); - gpio = (gpio & mask) | (val << shift); + reg = ioread32(gpio); + reg = (reg & mask) | (val << shift); - __handle_signal_inputs(bp, gpio); + __handle_signal_inputs(bp, reg); - iowrite32(gpio, &bp->sma->gpio1); + iowrite32(reg, gpio); spin_unlock_irqrestore(&bp->lock, flags); } -static ssize_t -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) +static int +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr) { - struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1]; + struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1]; enum ptp_ocp_sma_mode mode; int val; @@ -1730,18 +2226,35 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift) if (val < 0) return val; - if (mode != sma->mode && sma->fixed_mode) + if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE)) return -EOPNOTSUPP; + if (sma->fixed_fcn) { + if (val != ((sma_nr - 1) & 1)) + return -EOPNOTSUPP; + return 0; + } + + sma->disabled = !!(val & SMA_DISABLE); + if (mode != sma->mode) { - pr_err("Mode changes not supported yet.\n"); - return -EOPNOTSUPP; + if (mode == SMA_MODE_IN) + ptp_ocp_sma_store_output(bp, sma_nr, 0); + else + ptp_ocp_sma_store_inputs(bp, sma_nr, 0); + sma->mode = mode; } - if (sma->mode == SMA_MODE_IN) - ptp_ocp_sma_store_inputs(bp, val, shift); + if (!sma->fixed_dir) + val |= SMA_ENABLE; /* add enable bit */ + + if (sma->disabled) + val = 0; + + if (mode == SMA_MODE_IN) + ptp_ocp_sma_store_inputs(bp, sma_nr, val); else - ptp_ocp_sma_store_output(bp, val, shift); + ptp_ocp_sma_store_output(bp, sma_nr, val); return 0; } @@ -1753,7 +2266,7 @@ sma1_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 1, 0); + err = ptp_ocp_sma_store(bp, buf, 1); return err ? err : count; } @@ -1764,7 +2277,7 @@ sma2_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 2, 16); + err = ptp_ocp_sma_store(bp, buf, 2); return err ? err : count; } @@ -1775,7 +2288,7 @@ sma3_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 3, 0); + err = ptp_ocp_sma_store(bp, buf, 3); return err ? err : count; } @@ -1786,7 +2299,7 @@ sma4_store(struct device *dev, struct device_attribute *attr, struct ptp_ocp *bp = dev_get_drvdata(dev); int err; - err = ptp_ocp_sma_store(bp, buf, 4, 16); + err = ptp_ocp_sma_store(bp, buf, 4); return err ? err : count; } static DEVICE_ATTR_RW(sma1); @@ -1810,13 +2323,263 @@ available_sma_outputs_show(struct device *dev, } static DEVICE_ATTR_RO(available_sma_outputs); +#define EXT_ATTR_RO(_group, _name, _val) \ + struct dev_ext_attribute dev_attr_##_group##_val##_##_name = \ + { __ATTR_RO(_name), (void *)_val } +#define EXT_ATTR_RW(_group, _name, _val) \ + struct dev_ext_attribute dev_attr_##_group##_val##_##_name = \ + { __ATTR_RW(_name), (void *)_val } +#define to_ext_attr(x) container_of(x, struct dev_ext_attribute, attr) + +/* period [duty [phase [polarity]]] */ +static ssize_t +signal_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + struct ptp_ocp_signal s = { }; + int gen = (uintptr_t)ea->var; + int argc, err; + char **argv; + + argv = argv_split(GFP_KERNEL, buf, &argc); + if (!argv) + return -ENOMEM; + + err = -EINVAL; + s.duty = bp->signal[gen].duty; + s.phase = bp->signal[gen].phase; + s.period = bp->signal[gen].period; + s.polarity = bp->signal[gen].polarity; + + switch (argc) { + case 4: + argc--; + err = kstrtobool(argv[argc], &s.polarity); + if (err) + goto out; + fallthrough; + case 3: + argc--; + err = kstrtou64(argv[argc], 0, &s.phase); + if (err) + goto out; + fallthrough; + case 2: + argc--; + err = kstrtoint(argv[argc], 0, &s.duty); + if (err) + goto out; + fallthrough; + case 1: + argc--; + err = kstrtou64(argv[argc], 0, &s.period); + if (err) + goto out; + break; + default: + goto out; + } + + err = ptp_ocp_signal_set(bp, gen, &s); + if (err) + goto out; + + err = ptp_ocp_signal_enable(bp->signal_out[gen], gen, s.period != 0); + +out: + argv_free(argv); + return err ? err : count; +} + +static ssize_t +signal_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); + struct ptp_ocp_signal *signal; + struct timespec64 ts; + ssize_t count; + int i; + + i = (uintptr_t)ea->var; + signal = &bp->signal[i]; + + count = sysfs_emit(buf, "%llu %d %llu %d", signal->period, + signal->duty, signal->phase, signal->polarity); + + ts = ktime_to_timespec64(signal->start); + count += sysfs_emit_at(buf, count, " %ptT TAI\n", &ts); + + return count; +} +static EXT_ATTR_RW(signal, signal, 0); +static EXT_ATTR_RW(signal, signal, 1); +static EXT_ATTR_RW(signal, signal, 2); +static EXT_ATTR_RW(signal, signal, 3); + +static ssize_t +duty_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); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].duty); +} +static EXT_ATTR_RO(signal, duty, 0); +static EXT_ATTR_RO(signal, duty, 1); +static EXT_ATTR_RO(signal, duty, 2); +static EXT_ATTR_RO(signal, duty, 3); + +static ssize_t +period_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); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%llu\n", bp->signal[i].period); +} +static EXT_ATTR_RO(signal, period, 0); +static EXT_ATTR_RO(signal, period, 1); +static EXT_ATTR_RO(signal, period, 2); +static EXT_ATTR_RO(signal, period, 3); + +static ssize_t +phase_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); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%llu\n", bp->signal[i].phase); +} +static EXT_ATTR_RO(signal, phase, 0); +static EXT_ATTR_RO(signal, phase, 1); +static EXT_ATTR_RO(signal, phase, 2); +static EXT_ATTR_RO(signal, phase, 3); + +static ssize_t +polarity_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); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].polarity); +} +static EXT_ATTR_RO(signal, polarity, 0); +static EXT_ATTR_RO(signal, polarity, 1); +static EXT_ATTR_RO(signal, polarity, 2); +static EXT_ATTR_RO(signal, polarity, 3); + +static ssize_t +running_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); + int i = (uintptr_t)ea->var; + + return sysfs_emit(buf, "%d\n", bp->signal[i].running); +} +static EXT_ATTR_RO(signal, running, 0); +static EXT_ATTR_RO(signal, running, 1); +static EXT_ATTR_RO(signal, running, 2); +static EXT_ATTR_RO(signal, running, 3); + +static ssize_t +start_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); + int i = (uintptr_t)ea->var; + struct timespec64 ts; + + ts = ktime_to_timespec64(bp->signal[i].start); + return sysfs_emit(buf, "%llu.%lu\n", ts.tv_sec, ts.tv_nsec); +} +static EXT_ATTR_RO(signal, start, 0); +static EXT_ATTR_RO(signal, start, 1); +static EXT_ATTR_RO(signal, start, 2); +static EXT_ATTR_RO(signal, start, 3); + +static ssize_t +seconds_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dev_ext_attribute *ea = to_ext_attr(attr); + struct ptp_ocp *bp = dev_get_drvdata(dev); + int idx = (uintptr_t)ea->var; + u32 val; + int err; + + err = kstrtou32(buf, 0, &val); + if (err) + return err; + if (val > 0xff) + return -EINVAL; + + if (val) + val = (val << 8) | 0x1; + + iowrite32(val, &bp->freq_in[idx]->ctrl); + + return count; +} + +static ssize_t +seconds_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); + int idx = (uintptr_t)ea->var; + u32 val; + + val = ioread32(&bp->freq_in[idx]->ctrl); + if (val & 1) + val = (val >> 8) & 0xff; + else + val = 0; + + return sysfs_emit(buf, "%u\n", val); +} +static EXT_ATTR_RW(freq, seconds, 0); +static EXT_ATTR_RW(freq, seconds, 1); +static EXT_ATTR_RW(freq, seconds, 2); +static EXT_ATTR_RW(freq, seconds, 3); + +static ssize_t +frequency_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); + int idx = (uintptr_t)ea->var; + u32 val; + + val = ioread32(&bp->freq_in[idx]->status); + if (val & FREQ_STATUS_ERROR) + return sysfs_emit(buf, "error\n"); + if (val & FREQ_STATUS_OVERRUN) + return sysfs_emit(buf, "overrun\n"); + if (val & FREQ_STATUS_VALID) + return sysfs_emit(buf, "%lu\n", val & FREQ_STATUS_MASK); + return 0; +} +static EXT_ATTR_RO(freq, frequency, 0); +static EXT_ATTR_RO(freq, frequency, 1); +static EXT_ATTR_RO(freq, frequency, 2); +static EXT_ATTR_RO(freq, frequency, 3); + static ssize_t serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); + if (!bp->has_eeprom_data) + ptp_ocp_read_eeprom(bp); return sysfs_emit(buf, "%pM\n", bp->serial); } @@ -1974,7 +2737,122 @@ available_clock_sources_show(struct device *dev, } static DEVICE_ATTR_RO(available_clock_sources); -static struct attribute *timecard_attrs[] = { +static ssize_t +clock_status_drift_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->reg->status_drift); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} +static DEVICE_ATTR_RO(clock_status_drift); + +static ssize_t +clock_status_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->reg->status_offset); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} +static DEVICE_ATTR_RO(clock_status_offset); + +static ssize_t +tod_correction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + u32 val; + int res; + + val = ioread32(&bp->tod->adj_sec); + res = (val & ~INT_MAX) ? -1 : 1; + res *= (val & INT_MAX); + return sysfs_emit(buf, "%d\n", res); +} + +static ssize_t +tod_correction_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + unsigned long flags; + int err, res; + u32 val = 0; + + err = kstrtos32(buf, 0, &res); + if (err) + return err; + if (res < 0) { + res *= -1; + val |= BIT(31); + } + val |= res; + + spin_lock_irqsave(&bp->lock, flags); + iowrite32(val, &bp->tod->adj_sec); + spin_unlock_irqrestore(&bp->lock, flags); + + return count; +} +static DEVICE_ATTR_RW(tod_correction); + +#define _DEVICE_SIGNAL_GROUP_ATTRS(_nr) \ + static struct attribute *fb_timecard_signal##_nr##_attrs[] = { \ + &dev_attr_signal##_nr##_signal.attr.attr, \ + &dev_attr_signal##_nr##_duty.attr.attr, \ + &dev_attr_signal##_nr##_phase.attr.attr, \ + &dev_attr_signal##_nr##_period.attr.attr, \ + &dev_attr_signal##_nr##_polarity.attr.attr, \ + &dev_attr_signal##_nr##_running.attr.attr, \ + &dev_attr_signal##_nr##_start.attr.attr, \ + NULL, \ + } + +#define DEVICE_SIGNAL_GROUP(_name, _nr) \ + _DEVICE_SIGNAL_GROUP_ATTRS(_nr); \ + static const struct attribute_group \ + fb_timecard_signal##_nr##_group = { \ + .name = #_name, \ + .attrs = fb_timecard_signal##_nr##_attrs, \ +} + +DEVICE_SIGNAL_GROUP(gen1, 0); +DEVICE_SIGNAL_GROUP(gen2, 1); +DEVICE_SIGNAL_GROUP(gen3, 2); +DEVICE_SIGNAL_GROUP(gen4, 3); + +#define _DEVICE_FREQ_GROUP_ATTRS(_nr) \ + static struct attribute *fb_timecard_freq##_nr##_attrs[] = { \ + &dev_attr_freq##_nr##_seconds.attr.attr, \ + &dev_attr_freq##_nr##_frequency.attr.attr, \ + NULL, \ + } + +#define DEVICE_FREQ_GROUP(_name, _nr) \ + _DEVICE_FREQ_GROUP_ATTRS(_nr); \ + static const struct attribute_group \ + fb_timecard_freq##_nr##_group = { \ + .name = #_name, \ + .attrs = fb_timecard_freq##_nr##_attrs, \ +} + +DEVICE_FREQ_GROUP(freq1, 0); +DEVICE_FREQ_GROUP(freq2, 1); +DEVICE_FREQ_GROUP(freq3, 2); +DEVICE_FREQ_GROUP(freq4, 3); + +static struct attribute *fb_timecard_attrs[] = { &dev_attr_serialnum.attr, &dev_attr_gnss_sync.attr, &dev_attr_clock_source.attr, @@ -1985,38 +2863,119 @@ static struct attribute *timecard_attrs[] = { &dev_attr_sma4.attr, &dev_attr_available_sma_inputs.attr, &dev_attr_available_sma_outputs.attr, + &dev_attr_clock_status_drift.attr, + &dev_attr_clock_status_offset.attr, &dev_attr_irig_b_mode.attr, &dev_attr_utc_tai_offset.attr, &dev_attr_ts_window_adjust.attr, + &dev_attr_tod_correction.attr, NULL, }; -ATTRIBUTE_GROUPS(timecard); +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 }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal2_group }, + { .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal3_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq0_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq1_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq2_group }, + { .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq3_group }, + { }, +}; -static const char * -gpio_map(u32 gpio, u32 bit, const char *pri, const char *sec, const char *def) +static void +gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit, + const char *def) { - const char *ans; + int i; - if (gpio & (1 << bit)) - ans = pri; - else if (gpio & (1 << (bit + 16))) - ans = sec; - else - ans = def; - return ans; + for (i = 0; i < 4; i++) { + if (bp->sma[i].mode != SMA_MODE_IN) + continue; + if (map[i][0] & (1 << bit)) { + sprintf(buf, "sma%d", i + 1); + return; + } + } + if (!def) + def = "----"; + strcpy(buf, def); } static void -gpio_multi_map(char *buf, u32 gpio, u32 bit, - const char *pri, const char *sec, const char *def) +gpio_output_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit) { char *ans = buf; + int i; - strcpy(ans, def); - if (gpio & (1 << bit)) - ans += sprintf(ans, "%s ", pri); - if (gpio & (1 << (bit + 16))) - ans += sprintf(ans, "%s ", sec); + strcpy(ans, "----"); + for (i = 0; i < 4; i++) { + if (bp->sma[i].mode != SMA_MODE_OUT) + continue; + if (map[i][1] & (1 << bit)) + ans += sprintf(ans, "sma%d ", i + 1); + } +} + +static void +_signal_summary_show(struct seq_file *s, struct ptp_ocp *bp, int nr) +{ + struct signal_reg __iomem *reg = bp->signal_out[nr]->mem; + struct ptp_ocp_signal *signal = &bp->signal[nr]; + char label[8]; + bool on; + u32 val; + + if (!signal) + return; + + on = signal->running; + sprintf(label, "GEN%d", nr + 1); + seq_printf(s, "%7s: %s, period:%llu duty:%d%% phase:%llu pol:%d", + label, on ? " ON" : "OFF", + signal->period, signal->duty, signal->phase, + signal->polarity); + + val = ioread32(®->enable); + seq_printf(s, " [%x", val); + val = ioread32(®->status); + seq_printf(s, " %x]", val); + + seq_printf(s, " start:%llu\n", signal->start); +} + +static void +_frequency_summary_show(struct seq_file *s, int nr, + struct frequency_reg __iomem *reg) +{ + char label[8]; + bool on; + u32 val; + + if (!reg) + return; + + sprintf(label, "FREQ%d", nr + 1); + val = ioread32(®->ctrl); + on = val & 1; + val = (val >> 8) & 0xff; + seq_printf(s, "%7s: %s, sec:%u", + label, + on ? " ON" : "OFF", + val); + + val = ioread32(®->status); + if (val & FREQ_STATUS_ERROR) + seq_printf(s, ", error"); + if (val & FREQ_STATUS_OVERRUN) + seq_printf(s, ", overrun"); + if (val & FREQ_STATUS_VALID) + seq_printf(s, ", freq %lu Hz", val & FREQ_STATUS_MASK); + seq_printf(s, " reg:%x\n", val); } static int @@ -2024,40 +2983,72 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) { struct device *dev = s->private; struct ptp_system_timestamp sts; - u32 sma_in, sma_out, ctrl, val; struct ts_reg __iomem *ts_reg; struct timespec64 ts; struct ptp_ocp *bp; - const char *src; + u16 sma_val[4][2]; + char *src, *buf; + u32 ctrl, val; bool on, map; - char *buf; + int i; buf = (char *)__get_free_page(GFP_KERNEL); if (!buf) return -ENOMEM; bp = dev_get_drvdata(dev); - sma_in = ioread32(&bp->sma->gpio1); - sma_out = ioread32(&bp->sma->gpio2); 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); + + memset(sma_val, 0xff, sizeof(sma_val)); + if (bp->sma_map1) { + u32 reg; + + reg = ioread32(&bp->sma_map1->gpio1); + sma_val[0][0] = reg & 0xffff; + sma_val[1][0] = reg >> 16; + + reg = ioread32(&bp->sma_map1->gpio2); + sma_val[2][1] = reg & 0xffff; + sma_val[3][1] = reg >> 16; + + reg = ioread32(&bp->sma_map2->gpio1); + sma_val[2][0] = reg & 0xffff; + sma_val[3][0] = reg >> 16; + + reg = ioread32(&bp->sma_map2->gpio2); + sma_val[0][1] = reg & 0xffff; + sma_val[1][1] = reg >> 16; + } sma1_show(dev, NULL, buf); - seq_printf(s, " sma1: %s", buf); + seq_printf(s, " sma1: %04x,%04x %s", + sma_val[0][0], sma_val[0][1], buf); sma2_show(dev, NULL, buf); - seq_printf(s, " sma2: %s", buf); + seq_printf(s, " sma2: %04x,%04x %s", + sma_val[1][0], sma_val[1][1], buf); sma3_show(dev, NULL, buf); - seq_printf(s, " sma3: %s", buf); + seq_printf(s, " sma3: %04x,%04x %s", + sma_val[2][0], sma_val[2][1], buf); sma4_show(dev, NULL, buf); - seq_printf(s, " sma4: %s", buf); + seq_printf(s, " sma4: %04x,%04x %s", + sma_val[3][0], sma_val[3][1], buf); if (bp->ts0) { ts_reg = bp->ts0->mem; on = ioread32(&ts_reg->enable); - src = "GNSS"; + src = "GNSS1"; seq_printf(s, "%7s: %s, src: %s\n", "TS0", on ? " ON" : "OFF", src); } @@ -2065,17 +3056,33 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->ts1) { ts_reg = bp->ts1->mem; on = ioread32(&ts_reg->enable); - src = gpio_map(sma_in, 2, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 2, NULL); seq_printf(s, "%7s: %s, src: %s\n", "TS1", - on ? " ON" : "OFF", src); + on ? " ON" : "OFF", buf); } if (bp->ts2) { ts_reg = bp->ts2->mem; on = ioread32(&ts_reg->enable); - src = gpio_map(sma_in, 3, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 3, NULL); seq_printf(s, "%7s: %s, src: %s\n", "TS2", - on ? " ON" : "OFF", src); + on ? " ON" : "OFF", buf); + } + + if (bp->ts3) { + ts_reg = bp->ts3->mem; + on = ioread32(&ts_reg->enable); + gpio_input_map(buf, bp, sma_val, 6, NULL); + seq_printf(s, "%7s: %s, src: %s\n", "TS3", + on ? " ON" : "OFF", buf); + } + + if (bp->ts4) { + ts_reg = bp->ts4->mem; + on = ioread32(&ts_reg->enable); + gpio_input_map(buf, bp, sma_val, 7, NULL); + seq_printf(s, "%7s: %s, src: %s\n", "TS4", + on ? " ON" : "OFF", buf); } if (bp->pps) { @@ -2083,7 +3090,7 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) src = "PHC"; on = ioread32(&ts_reg->enable); map = !!(bp->pps_req_map & OCP_REQ_TIMESTAMP); - seq_printf(s, "%7s: %s, src: %s\n", "TS3", + seq_printf(s, "%7s: %s, src: %s\n", "TS5", on && map ? " ON" : "OFF", src); map = !!(bp->pps_req_map & OCP_REQ_PPS); @@ -2091,11 +3098,19 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) on && map ? " ON" : "OFF", src); } + if (bp->fw_cap & OCP_CAP_SIGNAL) + for (i = 0; i < 4; i++) + _signal_summary_show(s, bp, i); + + if (bp->fw_cap & OCP_CAP_FREQ) + for (i = 0; i < 4; i++) + _frequency_summary_show(s, i, bp->freq_in[i]); + if (bp->irig_out) { ctrl = ioread32(&bp->irig_out->ctrl); on = ctrl & IRIG_M_CTRL_ENABLE; val = ioread32(&bp->irig_out->status); - gpio_multi_map(buf, sma_out, 4, "sma3", "sma4", "----"); + gpio_output_map(buf, bp, sma_val, 4); seq_printf(s, "%7s: %s, error: %d, mode %d, out: %s\n", "IRIG", on ? " ON" : "OFF", val, (ctrl >> 16), buf); } @@ -2103,15 +3118,15 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->irig_in) { on = ioread32(&bp->irig_in->ctrl) & IRIG_S_CTRL_ENABLE; val = ioread32(&bp->irig_in->status); - src = gpio_map(sma_in, 4, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 4, NULL); seq_printf(s, "%7s: %s, error: %d, src: %s\n", "IRIG in", - on ? " ON" : "OFF", val, src); + on ? " ON" : "OFF", val, buf); } if (bp->dcf_out) { on = ioread32(&bp->dcf_out->ctrl) & DCF_M_CTRL_ENABLE; val = ioread32(&bp->dcf_out->status); - gpio_multi_map(buf, sma_out, 5, "sma3", "sma4", "----"); + gpio_output_map(buf, bp, sma_val, 5); seq_printf(s, "%7s: %s, error: %d, out: %s\n", "DCF", on ? " ON" : "OFF", val, buf); } @@ -2119,9 +3134,9 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) if (bp->dcf_in) { on = ioread32(&bp->dcf_in->ctrl) & DCF_S_CTRL_ENABLE; val = ioread32(&bp->dcf_in->status); - src = gpio_map(sma_in, 5, "sma1", "sma2", "----"); + gpio_input_map(buf, bp, sma_val, 5, NULL); seq_printf(s, "%7s: %s, error: %d, src: %s\n", "DCF in", - on ? " ON" : "OFF", val, src); + on ? " ON" : "OFF", val, buf); } if (bp->nmea_out) { @@ -2134,12 +3149,13 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) /* compute src for PPS1, used below. */ if (bp->pps_select) { val = ioread32(&bp->pps_select->gpio1); + src = &buf[80]; if (val & 0x01) - src = gpio_map(sma_in, 0, "sma1", "sma2", "----"); + gpio_input_map(src, bp, sma_val, 0, NULL); else if (val & 0x02) src = "MAC"; else if (val & 0x04) - src = "GNSS"; + src = "GNSS1"; else src = "----"; } else { @@ -2172,8 +3188,8 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) /* reuses PPS1 src from earlier */ seq_printf(s, "MAC PPS1 src: %s\n", src); - src = gpio_map(sma_in, 1, "sma1", "sma2", "GNSS2"); - seq_printf(s, "MAC PPS2 src: %s\n", src); + gpio_input_map(buf, bp, sma_val, 1, "GNSS2"); + seq_printf(s, "MAC PPS2 src: %s\n", buf); if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) { struct timespec64 sys_ts; @@ -2200,6 +3216,57 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary); +static int +ptp_ocp_tod_status_show(struct seq_file *s, void *data) +{ + struct device *dev = s->private; + struct ptp_ocp *bp; + u32 val; + int idx; + + bp = dev_get_drvdata(dev); + + val = ioread32(&bp->tod->ctrl); + if (!(val & TOD_CTRL_ENABLE)) { + seq_printf(s, "TOD Slave disabled\n"); + return 0; + } + seq_printf(s, "TOD Slave enabled, Control Register 0x%08X\n", val); + + idx = val & TOD_CTRL_PROTOCOL ? 4 : 0; + idx += (val >> 16) & 3; + seq_printf(s, "Protocol %s\n", ptp_ocp_tod_proto_name(idx)); + + idx = (val >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK; + seq_printf(s, "GNSS %s\n", ptp_ocp_tod_gnss_name(idx)); + + val = ioread32(&bp->tod->version); + seq_printf(s, "TOD Version %d.%d.%d\n", + val >> 24, (val >> 16) & 0xff, val & 0xffff); + + val = ioread32(&bp->tod->status); + seq_printf(s, "Status register: 0x%08X\n", val); + + val = ioread32(&bp->tod->adj_sec); + idx = (val & ~INT_MAX) ? -1 : 1; + idx *= (val & INT_MAX); + seq_printf(s, "Correction seconds: %d\n", idx); + + val = ioread32(&bp->tod->utc_status); + seq_printf(s, "UTC status register: 0x%08X\n", val); + seq_printf(s, "UTC offset: %d valid:%d\n", + val & TOD_STATUS_UTC_MASK, val & TOD_STATUS_UTC_VALID ? 1 : 0); + seq_printf(s, "Leap second info valid:%d, Leap second announce %d\n", + val & TOD_STATUS_LEAP_VALID ? 1 : 0, + val & TOD_STATUS_LEAP_ANNOUNCE ? 1 : 0); + + val = ioread32(&bp->tod->leap); + seq_printf(s, "Time to next leap second (in sec): %d\n", (s32) val); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ptp_ocp_tod_status); + static struct dentry *ptp_ocp_debugfs_root; static void @@ -2211,6 +3278,9 @@ ptp_ocp_debugfs_add_device(struct ptp_ocp *bp) bp->debug_root = d; debugfs_create_file("summary", 0444, bp->debug_root, &bp->dev, &ptp_ocp_summary_fops); + if (bp->tod) + debugfs_create_file("tod_status", 0444, bp->debug_root, + &bp->dev, &ptp_ocp_tod_status_fops); } static void @@ -2317,6 +3387,7 @@ ptp_ocp_complete(struct ptp_ocp *bp) { struct pps_device *pps; char buf[32]; + int i, err; if (bp->gnss_port != -1) { sprintf(buf, "ttyS%d", bp->gnss_port); @@ -2341,8 +3412,13 @@ ptp_ocp_complete(struct ptp_ocp *bp) if (pps) ptp_ocp_symlink(bp, pps->dev, "pps"); - if (device_add_groups(&bp->dev, timecard_groups)) - pr_err("device add groups failed\n"); + for (i = 0; bp->attr_tbl[i].cap; i++) { + if (!(bp->attr_tbl[i].cap & bp->fw_cap)) + continue; + err = sysfs_create_group(&bp->dev.kobj, bp->attr_tbl[i].group); + if (err) + return err; + } ptp_ocp_debugfs_add_device(bp); @@ -2389,20 +3465,15 @@ ptp_ocp_info(struct ptp_ocp *bp) u32 reg; ptp_ocp_phc_info(bp); - if (bp->tod) - ptp_ocp_tod_info(bp); - if (bp->image) { - u32 ver = ioread32(&bp->image->version); + dev_info(dev, "version %x\n", bp->fw_version); + if (bp->fw_version & 0xffff) + dev_info(dev, "regular image, version %d\n", + bp->fw_version & 0xffff); + else + dev_info(dev, "golden image, version %d\n", + bp->fw_version >> 16); - dev_info(dev, "version %x\n", ver); - if (ver & 0xffff) - dev_info(dev, "regular image, version %d\n", - ver & 0xffff); - else - dev_info(dev, "golden image, version %d\n", - ver >> 16); - } 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); @@ -2420,17 +3491,22 @@ static void ptp_ocp_detach_sysfs(struct ptp_ocp *bp) { struct device *dev = &bp->dev; + int i; sysfs_remove_link(&dev->kobj, "ttyGNSS"); sysfs_remove_link(&dev->kobj, "ttyMAC"); sysfs_remove_link(&dev->kobj, "ptp"); sysfs_remove_link(&dev->kobj, "pps"); - device_remove_groups(dev, timecard_groups); + if (bp->attr_tbl) + for (i = 0; bp->attr_tbl[i].cap; i++) + sysfs_remove_group(&dev->kobj, bp->attr_tbl[i].group); } static void ptp_ocp_detach(struct ptp_ocp *bp) { + int i; + ptp_ocp_debugfs_remove_device(bp); ptp_ocp_detach_sysfs(bp); if (timer_pending(&bp->watchdog)) @@ -2441,8 +3517,15 @@ ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_unregister_ext(bp->ts1); if (bp->ts2) ptp_ocp_unregister_ext(bp->ts2); + if (bp->ts3) + ptp_ocp_unregister_ext(bp->ts3); + if (bp->ts4) + ptp_ocp_unregister_ext(bp->ts4); if (bp->pps) ptp_ocp_unregister_ext(bp->pps); + 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) @@ -2461,6 +3544,7 @@ ptp_ocp_detach(struct ptp_ocp *bp) pci_free_irq_vectors(bp->pdev); if (bp->ptp) ptp_clock_unregister(bp->ptp); + kfree(bp->ptp_info.pin_config); device_unregister(&bp->dev); } @@ -2480,7 +3564,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "pci_enable_device\n"); - goto out_unregister; + goto out_free; } bp = devlink_priv(devlink); @@ -2493,7 +3577,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) * allow this - if not all of the IRQ's are returned, skip the * extra devices and just register the clock. */ - err = pci_alloc_irq_vectors(pdev, 1, 11, PCI_IRQ_MSI | PCI_IRQ_MSIX); + err = pci_alloc_irq_vectors(pdev, 1, 17, PCI_IRQ_MSI | PCI_IRQ_MSIX); if (err < 0) { dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err); goto out; @@ -2526,7 +3610,7 @@ out: pci_set_drvdata(pdev, NULL); out_disable: pci_disable_device(pdev); -out_unregister: +out_free: devlink_free(devlink); return err; } |