[PATCH 13/15] soc: octeontx2: Add support for CGX link management

2018-08-28 Thread sunil . kovvuri
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

2018-08-28 Thread sunil . kovvuri
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