[PATCH 13/15] soc: octeontx2: Add support for CGX link management
From: Linu Cherian CGX LMAC initialization, link status polling etc is done by low level secure firmware. For link management this patch adds a interface or communication mechanism between firmware and this kernel CGX driver. - Firmware interface specification is defined in cgx_fw_if.h. - Support to send/receive commands/events to/form firmware. - events/commands implemented * link up * link down * reading firmware version Signed-off-by: Linu Cherian Signed-off-by: Nithya Mani --- drivers/soc/marvell/octeontx2/cgx.c | 361 +- drivers/soc/marvell/octeontx2/cgx.h | 32 +++ drivers/soc/marvell/octeontx2/cgx_fw_if.h | 225 +++ 3 files changed, 614 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/marvell/octeontx2/cgx_fw_if.h diff --git a/drivers/soc/marvell/octeontx2/cgx.c b/drivers/soc/marvell/octeontx2/cgx.c index 70ca2e2f1..811422f 100644 --- a/drivers/soc/marvell/octeontx2/cgx.c +++ b/drivers/soc/marvell/octeontx2/cgx.c @@ -25,16 +25,43 @@ #define DRV_STRING "Marvell OcteonTX2 CGX/MAC Driver" #define DRV_VERSION"1.0" +/** + * struct lmac + * @wq_cmd_cmplt: waitq to keep the process blocked until cmd completion + * @cmd_lock: Lock to serialize the command interface + * @resp: command response + * @event_cb: callback for linkchange events + * @cmd_pend: flag set before new command is started + * flag cleared after command response is received + * @cgx: parent cgx port + * @lmac_id: lmac port id + * @name: lmac port name + */ +struct lmac { + wait_queue_head_t wq_cmd_cmplt; + struct mutex cmd_lock; + struct cgx_evt_sts resp; + struct cgx_event_cb event_cb; + bool cmd_pend; + struct cgx *cgx; + u8 lmac_id; + char *name; +}; + struct cgx { void __iomem*reg_base; struct pci_dev *pdev; u8 cgx_id; u8 lmac_count; + struct lmac *lmac_idmap[MAX_LMAC_PER_CGX]; struct list_headcgx_list; }; static LIST_HEAD(cgx_list); +/* CGX PHY management internal APIs */ +static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en); + /* Supported devices */ static const struct pci_device_id cgx_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) }, @@ -47,11 +74,24 @@ MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); MODULE_DEVICE_TABLE(pci, cgx_id_table); +static void cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val) +{ + writeq(val, cgx->reg_base + (lmac << 18) + offset); +} + static u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) { return readq(cgx->reg_base + (lmac << 18) + offset); } +static inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) +{ + if (!cgx || lmac_id >= MAX_LMAC_PER_CGX) + return NULL; + + return cgx->lmac_idmap[lmac_id]; +} + int cgx_get_cgx_cnt(void) { struct cgx *cgx_dev; @@ -87,18 +127,315 @@ void *cgx_get_pdata(int cgx_id) } EXPORT_SYMBOL(cgx_get_pdata); -static void cgx_lmac_init(struct cgx *cgx) +/* CGX Firmware interface low level support */ +static int cgx_fwi_cmd_send(struct cgx_cmd *cmd, struct cgx_evt_sts *rsp, + struct lmac *lmac) +{ + struct cgx *cgx = lmac->cgx; + union cgx_cmdreg creg; + union cgx_evtreg ereg; + struct device *dev; + int err = 0; + + /* Ensure no other command is in progress */ + err = mutex_lock_interruptible(>cmd_lock); + if (err) + return err; + + /* Ensure command register is free */ + creg.val = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); + if (creg.cmd.own != CGX_CMD_OWN_NS) { + err = -EBUSY; + goto unlock; + } + + /* Update ownership in command request */ + cmd->own = CGX_CMD_OWN_FIRMWARE; + + /* Mark this lmac as pending, before we start */ + lmac->cmd_pend = true; + + /* Start command in hardware */ + creg.cmd = *cmd; + cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, creg.val); + creg.val = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); + + /* Ensure command is completed without errors */ + if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend, + msecs_to_jiffies(CGX_CMD_TIMEOUT))) { + dev = >pdev->dev; + ereg.val = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG); + if (ereg.val) { + dev_err(dev, "cgx port %d:%d: No event for response\n", + cgx->cgx_id, lmac->lmac_id); + /* copy event */ + lmac->resp = ereg.evt_sts; + } else { + dev_err(dev, "cgx port %d:%d cmd
[PATCH 13/15] soc: octeontx2: Add support for CGX link management
From: Linu Cherian CGX LMAC initialization, link status polling etc is done by low level secure firmware. For link management this patch adds a interface or communication mechanism between firmware and this kernel CGX driver. - Firmware interface specification is defined in cgx_fw_if.h. - Support to send/receive commands/events to/form firmware. - events/commands implemented * link up * link down * reading firmware version Signed-off-by: Linu Cherian Signed-off-by: Nithya Mani --- drivers/soc/marvell/octeontx2/cgx.c | 361 +- drivers/soc/marvell/octeontx2/cgx.h | 32 +++ drivers/soc/marvell/octeontx2/cgx_fw_if.h | 225 +++ 3 files changed, 614 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/marvell/octeontx2/cgx_fw_if.h diff --git a/drivers/soc/marvell/octeontx2/cgx.c b/drivers/soc/marvell/octeontx2/cgx.c index 70ca2e2f1..811422f 100644 --- a/drivers/soc/marvell/octeontx2/cgx.c +++ b/drivers/soc/marvell/octeontx2/cgx.c @@ -25,16 +25,43 @@ #define DRV_STRING "Marvell OcteonTX2 CGX/MAC Driver" #define DRV_VERSION"1.0" +/** + * struct lmac + * @wq_cmd_cmplt: waitq to keep the process blocked until cmd completion + * @cmd_lock: Lock to serialize the command interface + * @resp: command response + * @event_cb: callback for linkchange events + * @cmd_pend: flag set before new command is started + * flag cleared after command response is received + * @cgx: parent cgx port + * @lmac_id: lmac port id + * @name: lmac port name + */ +struct lmac { + wait_queue_head_t wq_cmd_cmplt; + struct mutex cmd_lock; + struct cgx_evt_sts resp; + struct cgx_event_cb event_cb; + bool cmd_pend; + struct cgx *cgx; + u8 lmac_id; + char *name; +}; + struct cgx { void __iomem*reg_base; struct pci_dev *pdev; u8 cgx_id; u8 lmac_count; + struct lmac *lmac_idmap[MAX_LMAC_PER_CGX]; struct list_headcgx_list; }; static LIST_HEAD(cgx_list); +/* CGX PHY management internal APIs */ +static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en); + /* Supported devices */ static const struct pci_device_id cgx_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) }, @@ -47,11 +74,24 @@ MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); MODULE_DEVICE_TABLE(pci, cgx_id_table); +static void cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val) +{ + writeq(val, cgx->reg_base + (lmac << 18) + offset); +} + static u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) { return readq(cgx->reg_base + (lmac << 18) + offset); } +static inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) +{ + if (!cgx || lmac_id >= MAX_LMAC_PER_CGX) + return NULL; + + return cgx->lmac_idmap[lmac_id]; +} + int cgx_get_cgx_cnt(void) { struct cgx *cgx_dev; @@ -87,18 +127,315 @@ void *cgx_get_pdata(int cgx_id) } EXPORT_SYMBOL(cgx_get_pdata); -static void cgx_lmac_init(struct cgx *cgx) +/* CGX Firmware interface low level support */ +static int cgx_fwi_cmd_send(struct cgx_cmd *cmd, struct cgx_evt_sts *rsp, + struct lmac *lmac) +{ + struct cgx *cgx = lmac->cgx; + union cgx_cmdreg creg; + union cgx_evtreg ereg; + struct device *dev; + int err = 0; + + /* Ensure no other command is in progress */ + err = mutex_lock_interruptible(>cmd_lock); + if (err) + return err; + + /* Ensure command register is free */ + creg.val = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); + if (creg.cmd.own != CGX_CMD_OWN_NS) { + err = -EBUSY; + goto unlock; + } + + /* Update ownership in command request */ + cmd->own = CGX_CMD_OWN_FIRMWARE; + + /* Mark this lmac as pending, before we start */ + lmac->cmd_pend = true; + + /* Start command in hardware */ + creg.cmd = *cmd; + cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, creg.val); + creg.val = cgx_read(cgx, lmac->lmac_id, CGX_COMMAND_REG); + + /* Ensure command is completed without errors */ + if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend, + msecs_to_jiffies(CGX_CMD_TIMEOUT))) { + dev = >pdev->dev; + ereg.val = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG); + if (ereg.val) { + dev_err(dev, "cgx port %d:%d: No event for response\n", + cgx->cgx_id, lmac->lmac_id); + /* copy event */ + lmac->resp = ereg.evt_sts; + } else { + dev_err(dev, "cgx port %d:%d cmd