diff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard index 3ae41b7634ac..4ba45c6ee4a3 100644 --- a/Documentation/ABI/testing/sysfs-timecard +++ b/Documentation/ABI/testing/sysfs-timecard @@ -11,6 +11,35 @@ Contact: Jonathan Lemon Description: This directory contains the attributes of the Nth timecard registered. +What: /sys/class/timecard/ocpN/cpld_status +Date: July 2026 +Contact: Sagi Maimon +Description: (RO, root only) The flags set in the status register of the + TAP CPLD, space separated, or an empty line when none is + set. Only present on ADVA x1 TAP boards (PCI ID + 0xad5a:0x0410). + + ========== ================================================ + done the configuration flash holds a valid image and + it is active + busy an internal operation is in progress + failed the last ISC operation failed + ========== ================================================ + + For example "busy" while an operation runs, or "busy failed" + if a failure is latched while another one is in progress. + + A read arbitrates for the shared I2C bus and reprograms the + on-card mux, so it is restricted to root. The Lattice device + ID of the CPLD is read by the driver shortly after probe, + and again after a successful CPLD update, and reported from + that cached value as the fixed "cpld.id" version by + devlink dev info. + + New CPLD firmware is programmed with devlink dev flash, + selecting the "fw.cpld" component; see + Documentation/networking/devlink/ptp_ocp.rst. + What: /sys/class/timecard/ocpN/available_clock_sources Date: September 2021 Contact: Jonathan Lemon diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst index d4a83fdcff7f..1af780c811ee 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -99,6 +99,7 @@ parameters, info versions, and other features it supports. nfp octeontx2 prestera + ptp_ocp qed sfc stmmac diff --git a/Documentation/networking/devlink/ptp_ocp.rst b/Documentation/networking/devlink/ptp_ocp.rst new file mode 100644 index 000000000000..f94b759d9cd6 --- /dev/null +++ b/Documentation/networking/devlink/ptp_ocp.rst @@ -0,0 +1,75 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================== +ptp_ocp devlink support +======================== + +This document describes the devlink features implemented by the ``ptp_ocp`` +device driver: the info versions it reports and its flash update support. + +Info versions +============= + +The ``ptp_ocp`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``fw`` + - running + - Version of the firmware running on the card. Reported as ``loader`` + instead when the card is running the firmware loader. + * - ``board.id`` + - fixed + - Board identifier, read from the on-card EEPROM. + * - ``cpld.id`` + - fixed + - Lattice device ID (IDCODE) of the TAP CPLD, formatted as ``0x%08x``. + Only present on ADVA TimeCard X1 boards, which are the only ones + carrying that CPLD. Reading it claims the shared I2C bus and + reprograms the on-card mux, so the driver does that from its own + worker and reports the cached value here; the version is omitted + until that read has succeeded. The read is made once per binding + and again after a successful CPLD update. + * - ``fw.cpld`` + - running + - USERCODE of the image programmed into the TAP CPLD, formatted as + ``0x%08x``. Read together with ``cpld.id`` and reported the same + way. This is the component name to pass to ``devlink dev flash`` + to update the CPLD. + +Flash update +============ + +The driver implements ``devlink dev flash`` for two separate targets, +selected with the component name. + +.. list-table:: Flash components + :widths: 20 80 + + * - Component + - Description + * - (none) + - The card's own flash, written through the SPI controller the driver + exposes. The card runs the new image after its next reset. + * - ``fw.cpld`` + - The configuration flash of the TAP CPLD on ADVA TimeCard X1 boards, + programmed over I2C with the MachXO3 in-system programming commands + and activated with a REFRESH, so the new image runs immediately. + The image is the raw configuration bitstream. The only check the + driver makes is that its length is a non-zero multiple of the + 16-byte page size, so a container such as ``.jed`` has to be + converted first rather than passed through - one whose length + happens to be a multiple of 16 would be programmed as if it were + a bitstream. + +Programming the CPLD claims the shared I2C bus for the whole cycle, so +reads of the card's EEPROM block until it completes. Progress is reported +with the standard devlink status notifications. + +Example:: + + $ devlink dev flash pci/0000:02:00.0 file adva-cpld.bin component fw.cpld diff --git a/MAINTAINERS b/MAINTAINERS index df8ab9b82402..645c85e3a212 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20490,6 +20490,7 @@ OPENCOMPUTE PTP CLOCK DRIVER M: Vadim Fedorenko L: netdev@vger.kernel.org S: Maintained +F: Documentation/networking/devlink/ptp_ocp.rst F: drivers/ptp/ptp_ocp.c OPENCORES I2C BUS DRIVER diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 3d26ec1f7b9e..2802989e8494 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -20,10 +20,14 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #define PCI_DEVICE_ID_META_TIMECARD 0x0400 @@ -85,6 +89,8 @@ struct ptp_ocp_adva_info { u8 signals_nr; u8 freq_in_nr; const struct ocp_attr_group *attr_groups; + /* x1: TAP CPLD present */ + bool has_cpld; }; #define OCP_CTRL_ENABLE BIT(0) @@ -163,7 +169,8 @@ struct gpio_reg { u32 gpio1; u32 __pad0; u32 gpio2; - u32 __pad1; + /* adva_x1: I2C bus ownership register; reserved on other variants */ + u32 i2c_bus_ctrl; }; struct irig_master_reg { @@ -416,6 +423,31 @@ struct ptp_ocp { dpll_tracker tracker; int signals_nr; int freq_in_nr; + /* adva_x1 CPLD I2C (internal use only) */ + /* serialises CPLD operations */ + struct mutex cpld_lock; + /* guards cpld_i2c_adap_nr against the bus notifier */ + spinlock_t cpld_adap_lock; + /* I2C adapter nr; -1 if absent. Under cpld_adap_lock */ + int cpld_i2c_adap_nr; + /* claimed adapter; valid under cpld_lock */ + struct i2c_adapter *cpld_adap; + /* scratch, write half then read half; valid under cpld_lock */ + u8 *cpld_buf; + /* Lattice device ID; 0 if unread */ + u32 cpld_id; + /* USERCODE of the image in the part; valid once cpld_usercode_ok */ + u32 cpld_usercode; + /* cpld_usercode has been read since the last flash */ + bool cpld_usercode_ok; + /* one-shot ID read finished, successfully or not; under cpld_lock */ + bool cpld_id_tried; + /* failed ID read attempts so far; under cpld_lock */ + unsigned int cpld_id_attempts; + /* x1 TAP CPLD present */ + bool has_cpld; + /* EN_CFG_TP issued but not yet REFRESH'd */ + bool cpld_in_config_mode; }; #define OCP_REQ_TIMESTAMP BIT(0) @@ -452,6 +484,13 @@ static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static const struct ocp_sma_op ocp_adva_sma_op; static const struct ocp_sma_op ocp_adva_x1_sma_op; +/* Flash component naming the CPLD image, as reported by ->info_get(). */ +#define ADVA_CPLD_COMPONENT "fw.cpld" + +static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink, + const struct firmware *fw, + struct netlink_ext_ack *extack); + static const struct ocp_attr_group fb_timecard_groups[]; static const struct ocp_attr_group art_timecard_groups[]; @@ -1273,6 +1312,7 @@ static struct ocp_resource ocp_adva_x1_resource[] = { .signals_nr = 4, .freq_in_nr = 4, .attr_groups = adva_timecard_x1_groups, + .has_cpld = true, }, }, { } @@ -2133,6 +2173,21 @@ ptp_ocp_devlink_flash_update(struct devlink *devlink, const char *msg; int err; + if (params->component) { + if (!bp->has_cpld || + strcmp(params->component, ADVA_CPLD_COMPONENT)) { + NL_SET_ERR_MSG_MOD(extack, + "unsupported flash component"); + return -EOPNOTSUPP; + } + + err = adva_x1_cpld_flash(bp, devlink, params->fw, extack); + msg = err ? "Flash error" : "Flash complete"; + devlink_flash_update_status_notify(devlink, msg, + ADVA_CPLD_COMPONENT, 0, 0); + return err; + } + dev = ptp_ocp_find_flash(bp); if (!dev) { dev_err(&bp->pdev->dev, "Can't find Flash SPI adapter\n"); @@ -2155,10 +2210,13 @@ static int ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { + enum devlink_info_version_type ver_type = + DEVLINK_INFO_VERSION_TYPE_COMPONENT; struct ptp_ocp *bp = devlink_priv(devlink); const char *fw_image; char buf[32]; int err; + u32 id; fw_image = bp->fw_loader ? "loader" : "fw"; sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version); @@ -2166,6 +2224,39 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; + if (bp->has_cpld) { + /* Read by the worker, not here: this command is unprivileged + * and reading the ID claims the I2C bus. 0 means unread - + * skip it rather than fail, devlink discards the whole reply + * on error. + */ + id = READ_ONCE(bp->cpld_id); + if (id) { + sprintf(buf, "0x%08x", id); + err = devlink_info_version_fixed_put(req, "cpld.id", + buf); + if (err) + return err; + } + + /* The flashable component. Naming it here is what lets + * "devlink dev flash ... component fw.cpld" through, as the + * core matches the name against the versions reported here, + * so it is reported for every board that has the part and not + * only once its USERCODE has been read: a part left holding a + * bad image answers neither, and gating the component on the + * read would make that state unrecoverable. + */ + if (smp_load_acquire(&bp->cpld_usercode_ok)) + sprintf(buf, "0x%08x", READ_ONCE(bp->cpld_usercode)); + else + strscpy(buf, "unknown", sizeof(buf)); + err = devlink_info_version_running_put_ext(req, "fw.cpld", buf, + ver_type); + if (err) + return err; + } + if (!bp->has_eeprom_data) { ptp_ocp_read_eeprom(bp); if (!bp->has_eeprom_data) @@ -3199,6 +3290,8 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r) return err; ptp_ocp_sma_init(bp); + bp->has_cpld = info->has_cpld; + return ptp_ocp_init_clock(bp, &info->servo); } @@ -4226,6 +4319,794 @@ static const struct ocp_attr_group art_timecard_groups[] = { { }, }; +/* + * adva_x1 TAP CPLD (Lattice LCMXO3LF-2100) helpers. + * + * The card has two I2C controllers; Linux registers only 0x00150000. + * The i2c_bus_ctrl handshake re-routes what each is wired to: + * + * grant 0: 0x150000 -> EEPROMs 0x120000 -> TMC or M.2, MB's choice + * grant 1: 0x150000 -> TMC bus 0x120000 -> M.2 + * + * The PCA9548 at 0x74 and the CPLD at 0x40 behind its channel 0 are on the + * TMC bus, reachable only while the grant is held. The EEPROMs are not + * behind the adapter for that window, so a CPLD operation holds cpld_lock + * and the adapter lock to keep the EEPROM and nvmem paths off it. + */ + +#define ADVA_MUX_ADDR 0x74 +#define ADVA_CPLD_ADDR 0x40 +#define ADVA_MUX_CHANNEL 0 + +/* Longest command or reply; bounds each half of the per-claim scratch. */ +#define ADVA_CPLD_XFER_MAX 32 + +/* i2c_bus_ctrl carries a host-written request half and a firmware-written + * grant half. MBLAZE_GRANTED is what a granted register reads while our + * request still stands; the release write clears the request half, so from + * then on only the grant field can be tested. Measured on an ADVA + * TimeCard X1: the register reads 0x00000012 once the firmware has handed + * the segment back, about 670 ms after the release write, so the low half + * is not zero then and only the grant field is a valid test. + */ +#define MBLAZE_REQUEST 0x0000aaaaU +#define MBLAZE_GRANTED 0x5555aaaaU +#define MBLAZE_RELEASE 0x55550000U +#define MBLAZE_GRANT_MASK 0xffff0000U +#define MBLAZE_GRANT 0x55550000U +#define MBLAZE_RETRIES 200 +#define MBLAZE_RETRY_US 10000 + +/* Lattice LCMXO3LF ISC command codes */ +#define CPLD_CMD_READ_ID 0xE0000000UL +/* The USERCODE lives in the configuration flash that ERASE wipes, and the + * page writes carry it: after programming an image whose JEDEC UH field is + * 0x00000004 the part reads back 0x00000004, not an erased value. So there + * is no separate ISC_PROGRAM_USERCODE step here. + */ +#define CPLD_CMD_READ_USERCODE 0xC0000000UL +#define CPLD_CMD_READ_STATUS 0x3C000000UL +#define CPLD_CMD_EN_CFG_TP 0x74 /* enable config, transparent mode */ +#define CPLD_CMD_DIS_CFG 0x26 +#define CPLD_CMD_ERASE 0x0E +#define CPLD_CMD_RESET_ADDR 0x46 +#define CPLD_CMD_WRITE_PAGE 0x70 +#define CPLD_CMD_SET_DONE 0x5E +#define CPLD_CMD_REFRESH 0x79 +#define CPLD_PAGE_SIZE 16 +#define CPLD_POLL_US 10000 /* status poll interval while busy */ +/* Bounds how long a claim can hold the i2c root lock, not image validity: + * far above any bitstream this part takes. + */ +#define CPLD_MAX_IMAGE_SZ (256 * 1024) +#define CPLD_EXIT_CFG_TRIES 3 +#define CPLD_ERASE_MS 15000 /* config sector erase, datasheet max */ +#define CPLD_ID_MAX_ATTEMPTS 10 /* one per sync_work tick */ + +/* Status register bit positions (Lattice LCMXO3LF datasheet) */ +#define CPLD_STATUS_DONE BIT(8) +#define CPLD_STATUS_ENAB BIT(9) +#define CPLD_STATUS_BUSY BIT(12) +#define CPLD_STATUS_FAILED BIT(13) +#define CPLD_STATUS_ERR GENMASK(25, 23) + +/* + * Issue one I2C transaction on the TMC bus: @cmd if not negative, then + * @wlen bytes of @wdata (NULL sends zeros), then @rlen bytes read back. + * The message is built in the scratch buffer from adva_x1_bus_claim(), so + * a run of transfers costs one allocation, not one each. I2C_M_DMA_SAFE + * is not set: the two halves are not separately aligned, so let the core + * bounce. Caller holds the claim, hence __i2c_transfer(). + */ +static int adva_x1_i2c_xfer(struct ptp_ocp *bp, u8 addr, int cmd, + const void *wdata, u8 wlen, + void *rdata, u8 rlen) +{ + u8 *wbuf, *rbuf; + struct i2c_adapter *adap; + struct i2c_msg msgs[2]; + int nmsgs = 0, ret; + u16 hdr = cmd >= 0; + + lockdep_assert_held(&bp->cpld_lock); + + adap = bp->cpld_adap; + if (!adap || !bp->cpld_buf) + return -ENODEV; + + if (hdr + wlen > ADVA_CPLD_XFER_MAX || rlen > ADVA_CPLD_XFER_MAX) + return -EINVAL; + + wbuf = bp->cpld_buf; + rbuf = bp->cpld_buf + ADVA_CPLD_XFER_MAX; + + if (hdr + wlen) { + if (hdr) + wbuf[0] = cmd; + if (wdata) + memcpy(wbuf + hdr, wdata, wlen); + else + memset(wbuf + hdr, 0, wlen); + msgs[nmsgs++] = (struct i2c_msg){ + .addr = addr, + .flags = 0, + .len = hdr + wlen, + .buf = wbuf, + }; + } + if (rlen) { + msgs[nmsgs++] = (struct i2c_msg){ + .addr = addr, + .flags = I2C_M_RD, + .len = rlen, + .buf = rbuf, + }; + } + + ret = __i2c_transfer(adap, msgs, nmsgs); + if (ret != nmsgs) + return (ret < 0) ? ret : -EIO; + + if (rdata && rlen) + memcpy(rdata, rbuf, rlen); + + return 0; +} + +/* Hand the segment back, and wait for the firmware to drop the grant before + * the caller unlocks the adapter: a transfer queued behind us would + * otherwise be clocked out while the controller is still on the TMC bus, + * which is what holding the adapter lock across the grant exists to stop. + */ +static int adva_x1_mblaze_release(struct ptp_ocp *bp) +{ + u32 val = 0; + int i; + + if (!bp->pps_select) + return 0; + + iowrite32(MBLAZE_RELEASE, &bp->pps_select->i2c_bus_ctrl); + /* Flush the posted write, as the acquire path does. */ + ioread32(&bp->pps_select->i2c_bus_ctrl); + + for (i = 0; i < MBLAZE_RETRIES; i++) { + val = ioread32(&bp->pps_select->i2c_bus_ctrl); + if ((val & MBLAZE_GRANT_MASK) != MBLAZE_GRANT) + return 0; + usleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000); + } + + dev_err(&bp->pdev->dev, + "TMC bus still granted after release, i2c_bus_ctrl 0x%08x\n", + val); + + return -ETIMEDOUT; +} + +/* Acquire the shared I2C bus from the MicroBlaze firmware. Returns with no + * request outstanding on failure, so the firmware is never left granting a + * segment to a host that has given up waiting for it. + */ +static int adva_x1_mblaze_acquire(struct ptp_ocp *bp) +{ + u32 val; + int i; + + if (!bp->pps_select) + return -ENODEV; + + /* Drop a request left by a caller that died mid-sequence; cpld_lock + * keeps live ones out. The read back only flushes the posted write. + */ + iowrite32(0, &bp->pps_select->i2c_bus_ctrl); + ioread32(&bp->pps_select->i2c_bus_ctrl); + + iowrite32(MBLAZE_REQUEST, &bp->pps_select->i2c_bus_ctrl); + for (i = 0; i < MBLAZE_RETRIES; i++) { + usleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000); + val = ioread32(&bp->pps_select->i2c_bus_ctrl); + if (val == MBLAZE_GRANTED) + return 0; + } + + /* Drop the request we gave up on. Any error from the hand-back is + * subsumed by the -ETIMEDOUT we are already returning. + */ + adva_x1_mblaze_release(bp); + return -ETIMEDOUT; +} + +/* Route the host controller back to the EEPROMs and release the adapter. + * Safe after a failed claim: it also clears a request that was never granted. + * + * The adapter is unlocked even when the hand-back timed out: keeping the + * root lock would stall every other user of the controller for good, with + * no way to recover it. The error is returned instead, so the operation + * that held the claim fails rather than reporting success over a bus that + * may still be routed to the TMC segment. + */ +static int adva_x1_bus_release(struct ptp_ocp *bp) +{ + struct i2c_adapter *adap = bp->cpld_adap; + int err; + + if (!adap) + return 0; + + err = adva_x1_mblaze_release(bp); + bp->cpld_adap = NULL; + kfree(bp->cpld_buf); + bp->cpld_buf = NULL; + i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + i2c_put_adapter(adap); + + return err; +} + +static int adva_x1_i2c_adap_match(struct device *dev, const void *data) +{ + return !!i2c_verify_adapter(dev); +} + +/* + * Cache the adapter the CPLD hangs off. ptp_ocp_i2c_notifier_call() misses + * it when the i2c controller driver is already loaded: the adapter is then + * added from inside platform_device_register(), before bp->i2c_ctrl is set. + * Only direct children are searched, so a mux adapter cannot match. + * + * Called from the worker rather than once from probe: device_add() notifies + * before it links the device to its parent, so with asynchronous probing + * both the notifier and a single lookup here can miss the same adapter. + * Retrying costs a short klist walk per tick until one of them succeeds. + */ +static void adva_x1_cache_i2c_adap(struct ptp_ocp *bp) +{ + struct device *child; + + if (!bp->has_cpld || !bp->i2c_ctrl || + READ_ONCE(bp->cpld_i2c_adap_nr) >= 0) + return; + + child = device_find_child(&bp->i2c_ctrl->dev, NULL, + adva_x1_i2c_adap_match); + if (!child) + return; + + /* Re-test under the lock so this does not overwrite a number the + * notifier published meanwhile. It cannot tell that apart from a + * number the notifier has just invalidated, so an adapter removed in + * this window can still be cached; adva_x1_bus_claim() rejects it on + * the parent check and forgets it, and the next tick looks again. + */ + scoped_guard(spinlock, &bp->cpld_adap_lock) { + if (bp->cpld_i2c_adap_nr < 0) + bp->cpld_i2c_adap_nr = i2c_verify_adapter(child)->nr; + } + + put_device(child); +} + +/* + * Claim the TMC bus. The adapter lock is held across the handshake to keep + * the EEPROM, nvmem and at24 paths off the controller while it is routed + * away from the EEPROMs; an EEPROM read blocks until the claim is dropped. + */ +static int adva_x1_bus_claim(struct ptp_ocp *bp) +{ + struct i2c_adapter *adap; + int ret; + + lockdep_assert_held(&bp->cpld_lock); + + adap = i2c_get_adapter(READ_ONCE(bp->cpld_i2c_adap_nr)); + if (!adap) + return -ENODEV; + + /* The number is freed before the notifier clears it, so it can + * already be another adapter's. Check this is still ours, and drop + * a number that is not: otherwise every later claim fails the same + * way, where forgetting it lets the worker look the adapter up again. + */ + if (!bp->i2c_ctrl || adap->dev.parent != &bp->i2c_ctrl->dev) { + scoped_guard(spinlock, &bp->cpld_adap_lock) { + if (bp->cpld_i2c_adap_nr == adap->nr) + bp->cpld_i2c_adap_nr = -1; + } + i2c_put_adapter(adap); + return -ENODEV; + } + + /* One scratch buffer per claim rather than per transfer: a claim can + * cover many transfers. + */ + bp->cpld_buf = kzalloc(2 * ADVA_CPLD_XFER_MAX, GFP_KERNEL); + if (!bp->cpld_buf) { + i2c_put_adapter(adap); + return -ENOMEM; + } + + i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER); + bp->cpld_adap = adap; + + ret = adva_x1_mblaze_acquire(bp); + if (ret) + adva_x1_bus_release(bp); /* keeps the acquire error */ + + return ret; +} + +/* Select a mux channel, or deselect all with ch < 0 - the power-on state. + * The mux is on the TMC bus, so what it is left set to never affects the + * EEPROM paths. + */ +static int adva_x1_mux_select(struct ptp_ocp *bp, int ch) +{ + u8 val = (ch >= 0) ? BIT(ch) : 0; + + return adva_x1_i2c_xfer(bp, ADVA_MUX_ADDR, val, NULL, 0, NULL, 0); +} + +/* + * Argument bytes that follow an ISC opcode. Returns NULL with @nargs set + * when the arguments are all zero: adva_x1_i2c_xfer() zeroes the buffer. + * + * EN_CFG_TP, DIS_CFG and REFRESH take two operand bytes here where the SPI + * drivers in drivers/fpga send three. These are the frames the part on this + * board answers: the sequence below has programmed and refreshed it + * successfully over I2C, so the counts are kept as validated rather than + * aligned with the SPI framing. + */ +static const u8 *adva_x1_cpld_args(u8 cmd, u8 *nargs) +{ + static const u8 en_cfg_tp[] = { 0x08, 0x00 }; + /* cfg sector only */ + static const u8 erase_cfg[] = { 0x04, 0x00, 0x00 }; + + switch (cmd) { + case CPLD_CMD_EN_CFG_TP: + *nargs = sizeof(en_cfg_tp); + return en_cfg_tp; + case CPLD_CMD_ERASE: + *nargs = sizeof(erase_cfg); + return erase_cfg; + case CPLD_CMD_RESET_ADDR: + case CPLD_CMD_SET_DONE: + *nargs = 3; + return NULL; + case CPLD_CMD_DIS_CFG: + case CPLD_CMD_REFRESH: + *nargs = 2; + return NULL; + default: + *nargs = 0; + return NULL; + } +} + +/* Send an ISC command with the fixed arguments that belong to it. */ +static int adva_x1_cpld_write(struct ptp_ocp *bp, u8 cmd) +{ + const u8 *args; + u8 nargs; + + args = adva_x1_cpld_args(cmd, &nargs); + + return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, cmd, args, nargs, NULL, 0); +} + +/* + * Send a 4-byte command and read back without an intermediate STOP: two + * messages in one transfer is the Lattice write -> repeated START -> read, + * so no protocol-mangling flag is needed. + */ +static int adva_x1_cpld_cmd_read(struct ptp_ocp *bp, + u32 cmd_be, u8 *out, u8 out_len) +{ + __be32 cmd = cpu_to_be32(cmd_be); + + return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, -1, &cmd, 4, out, out_len); +} + +static int adva_x1_cpld_read_status(struct ptp_ocp *bp, u32 *status) +{ + u8 buf[4]; + int ret; + + ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_STATUS, buf, 4); + if (ret) + return ret; + *status = get_unaligned_be32(buf); + return 0; +} + +/* Poll the status register until the CPLD goes idle, or @max_ms elapses. + * The deadline is on wall time, so the I2C transactions count against it, + * and the status is read once more after it expires before giving up. + */ +static int adva_x1_cpld_wait_ready(struct ptp_ocp *bp, unsigned int max_ms) +{ + u32 status = 0; + int err = 0, ret; + + ret = read_poll_timeout(adva_x1_cpld_read_status, err, + err || (status & CPLD_STATUS_FAILED) || + !(status & CPLD_STATUS_BUSY), + CPLD_POLL_US, max_ms * USEC_PER_MSEC, false, + bp, &status); + if (ret) + return ret; + /* Keep the transport errno so it reaches userspace as-is; -EIO is + * reserved for the CPLD itself reporting FAILED. + */ + if (err) + return err; + if (status & CPLD_STATUS_FAILED) + return -EIO; + + return 0; +} + +/* Wait for BUSY to clear. Unlike adva_x1_cpld_wait_ready() a latched FAILED + * status is not an error here: this is used on the exit path, where an ISC + * command issued while the part is still erasing or programming may not + * latch, so the operation has to be waited out whatever its outcome. + */ +static int adva_x1_cpld_wait_idle(struct ptp_ocp *bp, unsigned int max_ms) +{ + u32 status = 0; + int err = 0, ret; + + ret = read_poll_timeout(adva_x1_cpld_read_status, err, + err || !(status & CPLD_STATUS_BUSY), + CPLD_POLL_US, max_ms * USEC_PER_MSEC, false, + bp, &status); + if (ret) + return ret; + + return err; +} + +/* Leave transparent configuration mode. + * + * A DIS_CFG issued while the part is still busy may not latch, so the wait + * has to succeed before the write is believed - an ACKed write after a + * timed-out wait says nothing about whether the part left the mode. Retry + * a few times rather than leaving it latched on one bad attempt; there is + * no other caller that would try again. + */ +static void adva_x1_cpld_exit_config(struct ptp_ocp *bp) +{ + int err, i; + + if (!bp->cpld_in_config_mode) + return; + + for (i = 0; i < CPLD_EXIT_CFG_TRIES; i++) { + err = adva_x1_cpld_wait_idle(bp, CPLD_ERASE_MS); + if (!err) + err = adva_x1_cpld_write(bp, CPLD_CMD_DIS_CFG); + if (!err) { + bp->cpld_in_config_mode = false; + return; + } + } + + dev_err(&bp->pdev->dev, "CPLD left in configuration mode: %d\n", err); +} + +/* + * Read the Lattice device ID and the image USERCODE under one bus claim. + * Done once, off the unprivileged devlink path, which reports the cached + * values. -EBUSY means cpld_lock is held, so the caller can retry rather + * than wait behind a long CPLD operation. + */ +static int adva_x1_cpld_read_id(struct ptp_ocp *bp) +{ + u8 data[4]; + int err, ret; + + if (!mutex_trylock(&bp->cpld_lock)) + return -EBUSY; + + ret = adva_x1_bus_claim(bp); + if (ret) + goto out; + /* A select reported as failed may still have been ACKed, so the + * deselect runs either way rather than handing the mux back with a + * channel possibly left open. + */ + ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL); + if (ret) + goto deselect; + ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_ID, data, 4); + if (ret) + goto deselect; + /* Publish the ID before the USERCODE is attempted: a part that does + * not answer 0xC0 must not cost us the device ID as well. + */ + WRITE_ONCE(bp->cpld_id, get_unaligned_be32(data)); + + ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_USERCODE, data, 4); + if (ret) + goto deselect; + WRITE_ONCE(bp->cpld_usercode, get_unaligned_be32(data)); + /* Pairs with the acquire in ptp_ocp_devlink_info_get(): the value has + * to be visible before the flag that declares it valid. + */ + smp_store_release(&bp->cpld_usercode_ok, true); +deselect: + err = adva_x1_mux_select(bp, -1); + if (!ret) + ret = err; + err = adva_x1_bus_release(bp); + if (!ret) + ret = err; +out: + /* Under cpld_lock, so an operation that resets this bookkeeping + * while holding the lock cannot have it overwritten afterwards by a + * worker that had already finished reading. + */ + if (!ret || ++bp->cpld_id_attempts >= CPLD_ID_MAX_ATTEMPTS) + bp->cpld_id_tried = true; + mutex_unlock(&bp->cpld_lock); + if (ret) + dev_dbg(&bp->pdev->dev, + "CPLD device ID unavailable: %d\n", ret); + + return ret; +} + +/* cpld_status - show the flags set in the TAP CPLD status register. */ +static ssize_t +cpld_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ptp_ocp *bp = dev_get_drvdata(dev); + int count = 0; + u32 st = 0; + int err, ret; + + /* A CPLD operation can hold cpld_lock a long time; stay killable. */ + ret = mutex_lock_interruptible(&bp->cpld_lock); + if (ret) + return ret; + + ret = adva_x1_bus_claim(bp); + if (ret) + goto out; + ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL); + if (ret) + goto deselect; + ret = adva_x1_cpld_read_status(bp, &st); +deselect: + err = adva_x1_mux_select(bp, -1); + if (!ret) + ret = err; + err = adva_x1_bus_release(bp); + if (!ret) + ret = err; +out: + mutex_unlock(&bp->cpld_lock); + if (ret) + return ret; + + /* Flags: the names of the bits that are set, nothing when none is, + * rather than a value per bit in one file. + */ + if (st & CPLD_STATUS_DONE) + count += sysfs_emit_at(buf, count, "done"); + if (st & CPLD_STATUS_BUSY) + count += sysfs_emit_at(buf, count, "%sbusy", count ? " " : ""); + if (st & CPLD_STATUS_FAILED) + count += sysfs_emit_at(buf, count, "%sfailed", + count ? " " : ""); + + return count + sysfs_emit_at(buf, count, "\n"); +} +static DEVICE_ATTR_ADMIN_RO(cpld_status); + +/* + * Program the CPLD configuration flash from @fw and activate it. Selected + * by the "fw.cpld" flash component; the SPI flash keeps the default path. + * Runs under cpld_lock with the i2c adapter lock held, so an EEPROM read + * blocks for as long as programming takes. + */ +static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink, + const struct firmware *fw, + struct netlink_ext_ack *extack) +{ + size_t offset; + int err, ret; + u32 st; + + if (!fw->size || fw->size % CPLD_PAGE_SIZE) { + NL_SET_ERR_MSG_MOD(extack, + "image must be a whole number of 16-byte pages"); + return -EINVAL; + } + if (fw->size > CPLD_MAX_IMAGE_SZ) { + NL_SET_ERR_MSG_MOD(extack, "image too large for this part"); + return -EINVAL; + } + + mutex_lock(&bp->cpld_lock); + + err = adva_x1_bus_claim(bp); + if (err) + goto unlock; + /* A select reported as failed may still have been ACKed, so hand the + * mux back deselected either way. + */ + err = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL); + if (err) + goto deselect; + + /* A previous flash may have failed to leave configuration mode. Now + * that the bus is claimed again, retry the exit before re-entering; + * a no-op when the flag is already clear. + */ + adva_x1_cpld_exit_config(bp); + + /* Set before EN_CFG_TP, not after: the CPLD may have entered config + * mode even if the write errors or the wait times out, and only this + * makes the exit path send DIS_CFG. A stray DIS_CFG is harmless; + * leaving config mode enabled is not. + */ + bp->cpld_in_config_mode = true; + + err = adva_x1_cpld_write(bp, CPLD_CMD_EN_CFG_TP); + if (!err) + err = adva_x1_cpld_wait_ready(bp, 5000); + if (err) + goto exit_config; + + /* Confirm the part really entered configuration mode. An enable + * frame that did not latch leaves an idle part reporting neither + * BUSY nor FAILED, the erase and page writes are ignored, and DONE + * is still set from the old image - so every later check passes and + * the update would be reported successful with the flash untouched. + */ + err = adva_x1_cpld_read_status(bp, &st); + if (err) + goto exit_config; + if (!(st & CPLD_STATUS_ENAB)) { + dev_err(&bp->pdev->dev, + "CPLD did not enter configuration mode, status 0x%08x\n", + st); + NL_SET_ERR_MSG_MOD(extack, + "CPLD did not enter configuration mode"); + err = -EIO; + goto exit_config; + } + + devlink_flash_update_status_notify(devlink, "Erasing", + ADVA_CPLD_COMPONENT, 0, 0); + err = adva_x1_cpld_write(bp, CPLD_CMD_ERASE); + if (!err) + err = adva_x1_cpld_wait_ready(bp, CPLD_ERASE_MS); + if (err) + goto exit_config; + + /* The old image is gone from here on, so stop reporting its + * identity even if the rest of the sequence fails. Written under + * cpld_lock, which adva_x1_cpld_read_id() also holds across its own + * bookkeeping, so the worker cannot resurrect any of it. + */ + WRITE_ONCE(bp->cpld_id, 0); + WRITE_ONCE(bp->cpld_usercode_ok, false); + bp->cpld_id_tried = false; + bp->cpld_id_attempts = 0; + + err = adva_x1_cpld_write(bp, CPLD_CMD_RESET_ADDR); + if (err) + goto exit_config; + + for (offset = 0; offset < fw->size; offset += CPLD_PAGE_SIZE) { + u8 args[3 + CPLD_PAGE_SIZE] = { 0x00, 0x00, 0x01 }; + + /* The loop holds cpld_lock and the i2c root lock for the + * whole image, so give a dying task a way out. The part is + * left unconfigured, which is recoverable: fw.cpld stays + * advertised so the image can be written again. + */ + if (fatal_signal_pending(current)) { + err = -EINTR; + goto exit_config; + } + + memcpy(&args[3], fw->data + offset, CPLD_PAGE_SIZE); + err = adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, CPLD_CMD_WRITE_PAGE, + args, sizeof(args), NULL, 0); + if (!err) + err = adva_x1_cpld_wait_ready(bp, 100); + if (err) + goto exit_config; + + if (!(offset % (CPLD_PAGE_SIZE * 64))) + devlink_flash_update_status_notify(devlink, + "Programming", + ADVA_CPLD_COMPONENT, + offset, fw->size); + } + devlink_flash_update_status_notify(devlink, "Programming", + ADVA_CPLD_COMPONENT, + fw->size, fw->size); + + err = adva_x1_cpld_write(bp, CPLD_CMD_SET_DONE); + if (!err) + err = adva_x1_cpld_wait_ready(bp, 1000); + if (err) + goto exit_config; + + err = adva_x1_cpld_read_status(bp, &st); + if (err) + goto exit_config; + if (!(st & CPLD_STATUS_DONE)) { + dev_err(&bp->pdev->dev, + "CPLD SET_DONE left status 0x%08x\n", st); + NL_SET_ERR_MSG_MOD(extack, "CPLD did not accept the image"); + err = -EIO; + goto exit_config; + } + + devlink_flash_update_status_notify(devlink, "Activating", + ADVA_CPLD_COMPONENT, 0, 0); + err = adva_x1_cpld_write(bp, CPLD_CMD_REFRESH); + if (err) + goto exit_config; + + /* REFRESH reboots the CPLD out of configuration mode, so the exit + * path must not send DIS_CFG afterwards even if a check below fails. + */ + bp->cpld_in_config_mode = false; + + /* The new image is already running, so a segment that is not back + * yet must not be reported as a failed update: retry the reselect + * rather than sampling the mux once at a fixed delay. + */ + msleep(1500); + err = read_poll_timeout(adva_x1_mux_select, err, !err, CPLD_POLL_US, + 3000 * USEC_PER_MSEC, false, + bp, ADVA_MUX_CHANNEL); + if (err) + goto deselect; + + err = adva_x1_cpld_wait_ready(bp, 3000); + if (err) + goto deselect; + + /* Require DONE set, not busy and no error code, as machxo2-spi.c does + * after a refresh: without it a CRC or preamble error reads back as a + * successful update. + */ + err = adva_x1_cpld_read_status(bp, &st); + if (err) + goto deselect; + if (!(st & CPLD_STATUS_DONE) || (st & CPLD_STATUS_BUSY) || + (st & CPLD_STATUS_ERR)) { + dev_err(&bp->pdev->dev, + "CPLD refresh left status 0x%08x\n", st); + NL_SET_ERR_MSG_MOD(extack, "CPLD did not come back configured"); + err = -EIO; + goto deselect; + } + +exit_config: + adva_x1_cpld_exit_config(bp); +deselect: + ret = adva_x1_mux_select(bp, -1); + if (!err) + err = ret; + ret = adva_x1_bus_release(bp); + if (!err) + err = ret; +unlock: + mutex_unlock(&bp->cpld_lock); + + return err; +} + static struct attribute *adva_timecard_attrs[] = { &dev_attr_serialnum.attr, &dev_attr_gnss_sync.attr, @@ -4274,6 +5155,7 @@ static struct attribute *adva_timecard_x1_attrs[] = { &dev_attr_ts_window_adjust.attr, &dev_attr_utc_tai_offset.attr, &dev_attr_tod_correction.attr, + &dev_attr_cpld_status.attr, NULL, }; @@ -4904,6 +5786,7 @@ ptp_ocp_detach(struct ptp_ocp *bp) clk_hw_unregister_fixed_rate(bp->i2c_clk); if (bp->n_irqs) pci_free_irq_vectors(bp->pdev); + mutex_destroy(&bp->cpld_lock); device_unregister(&bp->dev); } @@ -5052,6 +5935,17 @@ ptp_ocp_sync_work(struct work_struct *work) bp->sync = sync; + /* Resolve the adapter here rather than once in probe, where it can + * race the adapter's own registration, and read the ID as soon as it + * turns up. A claim can fail transiently - the firmware may not + * grant the segment straight after power-up - so adva_x1_cpld_read_id() + * retries a bounded number of times before giving up. + */ + adva_x1_cache_i2c_adap(bp); + if (bp->has_cpld && !READ_ONCE(bp->cpld_id_tried) && + READ_ONCE(bp->cpld_i2c_adap_nr) >= 0) + adva_x1_cpld_read_id(bp); + queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ); } @@ -5080,6 +5974,15 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto out_disable; + /* Before the first error path that calls ptp_ocp_detach(), so + * mutex_destroy() runs on an initialised mutex, and before + * ptp_ocp_register_resources(), so this sentinel cannot overwrite an + * adapter number the i2c notifier stores during registration. + */ + mutex_init(&bp->cpld_lock); + spin_lock_init(&bp->cpld_adap_lock); + bp->cpld_i2c_adap_nr = -1; + INIT_DELAYED_WORK(&bp->sync_work, ptp_ocp_sync_work); /* compat mode. @@ -5119,12 +6022,12 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (IS_ERR(bp->dpll)) { err = PTR_ERR(bp->dpll); dev_err(&pdev->dev, "dpll_device_alloc failed\n"); - goto out; + goto out_devlink; } err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp); if (err) - goto out; + goto out_dpll_put; for (i = 0; i < OCP_SMA_NUM; i++) { bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, @@ -5150,7 +6053,16 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]); dpll_pin_put(bp->sma[i].dpll_pin, &bp->sma[i].tracker); } + /* dpll_device_register() takes a second reference and stores bp; only + * dpll_device_unregister() drops it. Without this the device stays + * visible with a priv pointer into storage devlink_free() will release. + */ + dpll_device_unregister(bp->dpll, &dpll_ops, bp); +out_dpll_put: dpll_device_put(bp->dpll, &bp->tracker); +out_devlink: + /* detach() destroys cpld_lock; no devlink op may still reach it */ + devlink_unregister(devlink); out: ptp_ocp_detach(bp); out_disable: @@ -5196,6 +6108,7 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb, unsigned long action, void *data) { struct device *dev, *child = data; + struct i2c_adapter *adap; struct ptp_ocp *bp; bool add; @@ -5219,11 +6132,25 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb, found: bp = dev_get_drvdata(dev); - if (add) + adap = i2c_verify_adapter(child); + if (add) { ptp_ocp_symlink(bp, child, "i2c"); - else + /* Only the card's own controller: a mux child added on it + * reaches ptp_ocp through the same walk. Numbers are + * recycled, so the claim re-checks the parent itself. + */ + if (bp->i2c_ctrl && adap->dev.parent == &bp->i2c_ctrl->dev) { + scoped_guard(spinlock, &bp->cpld_adap_lock) + bp->cpld_i2c_adap_nr = adap->nr; + } + } else { + /* invalidate before free, but only for the cached adapter */ + scoped_guard(spinlock, &bp->cpld_adap_lock) { + if (bp->cpld_i2c_adap_nr == adap->nr) + bp->cpld_i2c_adap_nr = -1; + } sysfs_remove_link(&bp->dev.kobj, "i2c"); - + } return 0; }