This patch adds net_device_ops.ndo_open and .ndo_stop
callback. These function is called when network device
activation and deactivation.

Signed-off-by: Taku Izumi <izumi.t...@jp.fujitsu.com>
---
 drivers/platform/x86/fjes/fjes.h      |   2 +-
 drivers/platform/x86/fjes/fjes_hw.c   | 150 ++++++++++++++++++++
 drivers/platform/x86/fjes/fjes_hw.h   |  30 ++++
 drivers/platform/x86/fjes/fjes_main.c | 257 ++++++++++++++++++++++++++++++++++
 drivers/platform/x86/fjes/fjes_regs.h |  16 +++
 5 files changed, 454 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/fjes/fjes.h b/drivers/platform/x86/fjes/fjes.h
index 3515572..7f03eb7 100644
--- a/drivers/platform/x86/fjes/fjes.h
+++ b/drivers/platform/x86/fjes/fjes.h
@@ -30,7 +30,7 @@
 #define FJES_ACPI_SYMBOL       "Extended Socket"
 #define FJES_MAX_QUEUES                1
 #define FJES_TX_RETRY_INTERVAL (20 * HZ)
-
+#define FJES_OPEN_ZONE_UPDATE_WAIT     (300) /* msec */
 
 /* board specific private data structure */
 struct fjes_adapter {
diff --git a/drivers/platform/x86/fjes/fjes_hw.c 
b/drivers/platform/x86/fjes/fjes_hw.c
index 5c68541..e2137d8 100644
--- a/drivers/platform/x86/fjes/fjes_hw.c
+++ b/drivers/platform/x86/fjes/fjes_hw.c
@@ -564,6 +564,7 @@ int fjes_hw_register_buff_addr(struct fjes_hw *hw, int 
dest_epid,
        return result;
 }
 
+
 int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int dest_epid)
 {
        union fjes_device_command_req *req_buf = hw->hw_info.req_buf;
@@ -650,6 +651,25 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *hw, int 
dest_epid)
        return result;
 }
 
+int fjes_hw_raise_interrupt(struct fjes_hw *hw, int dest_epid,
+               enum REG_ICTL_MASK  mask)
+{
+       u32 ig = mask | dest_epid;
+
+       wr32(XSCT_IG, cpu_to_le32(ig));
+
+       return 0;
+}
+
+u32 fjes_hw_capture_interrupt_status(struct fjes_hw *hw)
+{
+       u32 cur_is;
+
+       cur_is = rd32(XSCT_IS);
+
+       return cur_is;
+}
+
 void fjes_hw_set_irqmask(struct fjes_hw *hw, enum REG_ICTL_MASK intr_mask,
                bool mask)
 {
@@ -659,3 +679,133 @@ void fjes_hw_set_irqmask(struct fjes_hw *hw, enum 
REG_ICTL_MASK intr_mask,
                wr32(XSCT_IMC, intr_mask);
 }
 
+bool fjes_hw_epid_is_same_zone(struct fjes_hw *hw, int epid)
+{
+       if (epid >= hw->max_epid)
+               return false;
+
+       if ((hw->ep_shm_info[epid].es_status !=
+                       FJES_ZONING_STATUS_ENABLE) ||
+               (hw->ep_shm_info[hw->my_epid].zone ==
+                       FJES_ZONING_ZONE_TYPE_NONE))
+               return false;
+       else
+               return (hw->ep_shm_info[epid].zone ==
+                               hw->ep_shm_info[hw->my_epid].zone);
+
+}
+
+int fjes_hw_epid_is_shared(struct fjes_device_shared_info *share,
+               int dest_epid)
+{
+       int value = false;
+
+       if (dest_epid < share->epnum)
+               value = share->ep_status[dest_epid];
+
+       return value;
+}
+
+static bool fjes_hw_epid_is_stop_requested(struct fjes_hw *hw, int src_epid)
+{
+       return test_bit(src_epid, &hw->txrx_stop_req_bit);
+}
+
+static bool fjes_hw_epid_is_stop_process_done(struct fjes_hw *hw, int src_epid)
+{
+       return (hw->ep_shm_info[src_epid].tx.info->v1i.rx_status &
+                       FJES_RX_STOP_REQ_DONE);
+}
+
+enum ep_partner_status
+fjes_hw_get_partner_ep_status(struct fjes_hw *hw, int epid)
+{
+       enum ep_partner_status status;
+
+       if (fjes_hw_epid_is_shared(hw->hw_info.share, epid)) {
+               if (fjes_hw_epid_is_stop_requested(hw, epid))
+                       status = EP_PARTNER_WAITING;
+               else {
+                       if (fjes_hw_epid_is_stop_process_done(hw, epid))
+                               status = EP_PARTNER_COMPLETE;
+                       else
+                               status = EP_PARTNER_SHARED;
+               }
+       } else {
+               status = EP_PARTNER_UNSHARE;
+       }
+
+       return status;
+}
+
+void fjes_hw_raise_epstop(struct fjes_hw *hw)
+{
+       int epidx;
+       enum ep_partner_status status;
+
+       for (epidx = 0; epidx < hw->max_epid; epidx++) {
+               if (epidx == hw->my_epid)
+                       continue;
+
+               status = fjes_hw_get_partner_ep_status(hw, epidx);
+               switch (status) {
+               case EP_PARTNER_SHARED:
+                       fjes_hw_raise_interrupt(hw, epidx,
+                                       REG_ICTL_MASK_TXRX_STOP_REQ);
+                       break;
+               default:
+                       break;
+               }
+
+               set_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit);
+               set_bit(epidx, &hw->txrx_stop_req_bit);
+
+               hw->ep_shm_info[epidx].tx.info->v1i.rx_status |=
+                               FJES_RX_STOP_REQ_REQUEST;
+
+       }
+
+}
+
+int fjes_hw_wait_epstop(struct fjes_hw *hw)
+{
+       int epidx;
+       int wait_time = 0;
+       enum ep_partner_status status;
+       union ep_buffer_info *info;
+
+       while (hw->hw_info.buffer_unshare_reserve_bit &&
+                  (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000)) {
+
+               for (epidx = 0; epidx < hw->max_epid; epidx++) {
+                       if (epidx == hw->my_epid)
+                               continue;
+                       status = fjes_hw_epid_is_shared(hw->hw_info.share,
+                                       epidx);
+                       info = hw->ep_shm_info[epidx].rx.info;
+                       if ((!status ||
+                                (info->v1i.rx_status &
+                                       FJES_RX_STOP_REQ_DONE)) &&
+                               test_bit(epidx,
+                                &hw->hw_info.buffer_unshare_reserve_bit)) {
+                               clear_bit(epidx,
+                                &hw->hw_info.buffer_unshare_reserve_bit);
+                       }
+               }
+
+               msleep(100);
+               wait_time += 100;
+
+       }
+
+       for (epidx = 0; epidx < hw->max_epid; epidx++) {
+               if (epidx == hw->my_epid)
+                       continue;
+               if (test_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit))
+                       clear_bit(epidx,
+                               &hw->hw_info.buffer_unshare_reserve_bit);
+       }
+
+       return (wait_time < FJES_COMMAND_EPSTOP_WAIT_TIMEOUT * 1000)
+                       ? 0 : -EBUSY;
+}
diff --git a/drivers/platform/x86/fjes/fjes_hw.h 
b/drivers/platform/x86/fjes/fjes_hw.h
index 2528992..170bcfb 100644
--- a/drivers/platform/x86/fjes/fjes_hw.h
+++ b/drivers/platform/x86/fjes/fjes_hw.h
@@ -35,6 +35,7 @@ struct fjes_hw;
 #define FJES_DEVICE_RESET_TIMEOUT  ((17 + 1) * 3) /* sec */
 #define FJES_COMMAND_REQ_TIMEOUT  (5 + 1) /* sec */
 #define FJES_COMMAND_REQ_BUFF_TIMEOUT  (8 * 3) /* sec */
+#define FJES_COMMAND_EPSTOP_WAIT_TIMEOUT       (1) /* sec */
 
 #define FJES_CMD_REQ_ERR_INFO_PARAM  (0x0001)
 #define FJES_CMD_REQ_ERR_INFO_STATUS (0x0002)
@@ -42,6 +43,17 @@ struct fjes_hw;
 #define FJES_CMD_REQ_RES_CODE_NORMAL (0)
 #define FJES_CMD_REQ_RES_CODE_BUSY   (1)
 
+#define FJES_ZONING_STATUS_DISABLE     (0x00)
+#define FJES_ZONING_STATUS_ENABLE      (0x01)
+#define FJES_ZONING_STATUS_INVALID     (0xFF)
+
+#define FJES_ZONING_ZONE_TYPE_NONE (0xFF)
+
+#define FJES_RX_STOP_REQ_NONE          (0x0)
+#define FJES_RX_STOP_REQ_DONE          (0x1)
+#define FJES_RX_STOP_REQ_REQUEST       (0x2)
+#define FJES_RX_POLL_WORK                      (0x4)
+
 #define EP_BUFFER_SIZE \
        (((sizeof(union ep_buffer_info) + (128 * (64 * 1024))) \
                / EP_BUFFER_INFO_SIZE) * EP_BUFFER_INFO_SIZE)
@@ -77,6 +89,15 @@ struct esmem_frame_t {
        u8 frame_data[];
 };
 
+/* EP partner status */
+enum ep_partner_status {
+       EP_PARTNER_UNSHARE,
+       EP_PARTNER_SHARED,
+       EP_PARTNER_WAITING,
+       EP_PARTNER_COMPLETE,
+       EP_PARTNER_STATUS_MAX,
+};
+
 /* shared status region */
 struct fjes_device_shared_info {
        int epnum;
@@ -281,6 +302,15 @@ int fjes_hw_unregister_buff_addr(struct fjes_hw *, int);
 void fjes_hw_init_command_registers(struct fjes_hw *,
                struct fjes_device_command_param *);
 void fjes_hw_setup_epbuf(struct epbuf_handler *, u8 *, u32);
+int fjes_hw_raise_interrupt(struct fjes_hw *, int, enum REG_ICTL_MASK);
 void fjes_hw_set_irqmask(struct fjes_hw *, enum REG_ICTL_MASK, bool);
+u32 fjes_hw_capture_interrupt_status(struct fjes_hw *);
+void fjes_hw_raise_epstop(struct fjes_hw *);
+int fjes_hw_wait_epstop(struct fjes_hw *);
+enum ep_partner_status
+       fjes_hw_get_partner_ep_status(struct fjes_hw *, int);
+
+bool fjes_hw_epid_is_same_zone(struct fjes_hw *, int);
+int fjes_hw_epid_is_shared(struct fjes_device_shared_info *, int);
 
 #endif /* FJES_HW_H_ */
diff --git a/drivers/platform/x86/fjes/fjes_main.c 
b/drivers/platform/x86/fjes/fjes_main.c
index f38e0af..14a72ec 100644
--- a/drivers/platform/x86/fjes/fjes_main.c
+++ b/drivers/platform/x86/fjes/fjes_main.c
@@ -24,6 +24,7 @@
 #include <linux/nls.h>
 #include <linux/platform_device.h>
 #include <linux/netdevice.h>
+#include <linux/interrupt.h>
 
 #include "fjes.h"
 
@@ -44,6 +45,14 @@ MODULE_DESCRIPTION("FUJITSU Extended Socket Network Device 
Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
+static int fjes_request_irq(struct fjes_adapter *);
+static void fjes_free_irq(struct fjes_adapter *);
+
+static int fjes_open(struct net_device *);
+static int fjes_close(struct net_device *);
+static int fjes_setup_resources(struct fjes_adapter *);
+static void fjes_free_resources(struct fjes_adapter *);
+static irqreturn_t fjes_intr(int, void*);
 
 static int fjes_acpi_add(struct acpi_device *);
 static int fjes_acpi_remove(struct acpi_device *);
@@ -173,10 +182,258 @@ static acpi_status fjes_get_acpi_resource(struct 
acpi_resource *acpi_res,
        return AE_OK;
 }
 
+static int fjes_request_irq(struct fjes_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int result = -1;
+
+       if (!adapter->irq_registered) {
+               result = request_irq(adapter->hw.hw_res.irq, fjes_intr,
+                                       IRQF_SHARED, netdev->name, adapter);
+               if (result)
+                       adapter->irq_registered = false;
+               else
+                       adapter->irq_registered = true;
+       }
+
+       return result;
+}
+
+static void fjes_free_irq(struct fjes_adapter *adapter)
+{
+       struct fjes_hw *hw = &adapter->hw;
+
+
+       fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, true);
+
+       if (adapter->irq_registered) {
+               free_irq(adapter->hw.hw_res.irq, adapter);
+               adapter->irq_registered = false;
+       }
+}
+
 static const struct net_device_ops fjes_netdev_ops = {
+       .ndo_open               = fjes_open,
+       .ndo_stop               = fjes_close,
 };
 
 /*
+ *  fjes_open - Called when a network interface is made active
+ */
+static int fjes_open(struct net_device *netdev)
+{
+       int result;
+       struct fjes_adapter *adapter = netdev_priv(netdev);
+       struct fjes_hw *hw = &adapter->hw;
+
+       if (adapter->open_guard)
+               return -ENXIO;
+
+       result = fjes_setup_resources(adapter);
+       if (result)
+               goto err_setup_res;
+
+       hw->txrx_stop_req_bit = 0;
+       hw->epstop_req_bit = 0;
+
+       fjes_hw_capture_interrupt_status(hw);
+
+       result = fjes_request_irq(adapter);
+       if (result)
+               goto err_req_irq;
+
+       fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, false);
+
+       netif_tx_start_all_queues(netdev);
+       netif_carrier_on(netdev);
+
+       return 0;
+
+err_req_irq:
+       fjes_free_irq(adapter);
+
+err_setup_res:
+       fjes_free_resources(adapter);
+       return result;
+
+}
+
+/*
+ *  fjes_close - Disables a network interface
+ */
+static int fjes_close(struct net_device *netdev)
+{
+       struct fjes_adapter *adapter = netdev_priv(netdev);
+       struct fjes_hw *hw = &adapter->hw;
+       int epidx;
+
+       netif_tx_stop_all_queues(netdev);
+       netif_carrier_off(netdev);
+
+       fjes_hw_raise_epstop(hw);
+
+       for (epidx = 0; epidx < hw->max_epid; epidx++) {
+               if (epidx == hw->my_epid)
+                       continue;
+
+               adapter->hw.ep_shm_info[epidx].tx.info->v1i.rx_status &=
+                       ~FJES_RX_POLL_WORK;
+       }
+
+       fjes_free_irq(adapter);
+
+       fjes_hw_wait_epstop(hw);
+
+       fjes_free_resources(adapter);
+
+       return 0;
+}
+
+static int fjes_setup_resources(struct fjes_adapter *adapter)
+{
+       int epidx;
+       struct ep_share_mem_info *buf_pair;
+       int result;
+       struct fjes_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
+
+
+       mutex_lock(&hw->hw_info.lock);
+       result = fjes_hw_request_info(hw);
+       switch (result) {
+       case 0:
+               for (epidx = 0; epidx < hw->max_epid; epidx++) {
+                       hw->ep_shm_info[epidx].es_status =
+                           hw->hw_info.res_buf->info.info[epidx].es_status;
+                       hw->ep_shm_info[epidx].zone =
+                           hw->hw_info.res_buf->info.info[epidx].zone;
+               }
+               break;
+       default:
+       case -ENOMSG:
+       case -EBUSY:
+               adapter->force_reset = true;
+
+               mutex_unlock(&hw->hw_info.lock);
+               return result;
+       }
+       mutex_unlock(&hw->hw_info.lock);
+
+
+       for (epidx = 0; epidx < (hw->max_epid); epidx++) {
+               if ((epidx != hw->my_epid) &&
+                       (hw->ep_shm_info[epidx].es_status ==
+                               FJES_ZONING_STATUS_ENABLE)) {
+                       fjes_hw_raise_interrupt(hw, epidx,
+                                       REG_ICTL_MASK_INFO_UPDATE);
+               }
+       }
+
+       msleep(FJES_OPEN_ZONE_UPDATE_WAIT * hw->max_epid);
+
+
+       for (epidx = 0; epidx < (hw->max_epid); epidx++) {
+               if (epidx == hw->my_epid)
+                       continue;
+
+               buf_pair = &(hw->ep_shm_info[epidx]);
+
+               fjes_hw_setup_epbuf(&(buf_pair->tx), netdev->dev_addr,
+                               netdev->mtu);
+
+               if (fjes_hw_epid_is_same_zone(hw, epidx)) {
+                       mutex_lock(&hw->hw_info.lock);
+                       result = fjes_hw_register_buff_addr(hw, epidx,
+                                       buf_pair);
+                       mutex_unlock(&hw->hw_info.lock);
+
+                       switch (result) {
+                       case 0:
+                               break;
+                       case -ENOMSG:
+                       case -EBUSY:
+                       default:
+                               adapter->force_reset = true;
+                               return result;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void fjes_free_resources(struct fjes_adapter *adapter)
+{
+       int epidx;
+       struct ep_share_mem_info *buf_pair;
+       int result;
+       struct fjes_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
+       struct fjes_device_command_param param;
+       bool reset_flag = false;
+
+       for (epidx = 0; epidx < hw->max_epid; epidx++) {
+               if (epidx == hw->my_epid)
+                       continue;
+
+               mutex_lock(&hw->hw_info.lock);
+               result = fjes_hw_unregister_buff_addr(hw, epidx);
+               mutex_unlock(&hw->hw_info.lock);
+
+               if (result)
+                       reset_flag = true;
+
+               buf_pair = &hw->ep_shm_info[epidx];
+
+               fjes_hw_setup_epbuf(&buf_pair->tx, netdev->dev_addr,
+                               netdev->mtu);
+
+               clear_bit(epidx, &hw->txrx_stop_req_bit);
+       }
+
+       if (reset_flag || adapter->force_reset) {
+
+               result = fjes_hw_reset(hw);
+
+               adapter->force_reset = false;
+
+               if (result)
+                       adapter->open_guard = true;
+
+               hw->hw_info.buffer_share_bit = 0;
+
+               memset((void *)&param, 0, sizeof(param));
+
+               param.req_len = hw->hw_info.req_buf_size;
+               param.req_start = __pa(hw->hw_info.req_buf);
+               param.res_len = hw->hw_info.res_buf_size;
+               param.res_start = __pa(hw->hw_info.res_buf);
+               param.share_start = __pa(hw->hw_info.share->ep_status);
+
+               fjes_hw_init_command_registers(hw, &param);
+       }
+
+
+}
+
+static irqreturn_t fjes_intr(int irq, void *data)
+{
+       struct fjes_adapter *adapter = data;
+       struct fjes_hw *hw = &adapter->hw;
+       irqreturn_t ret;
+       u32 icr;
+
+       icr = fjes_hw_capture_interrupt_status(hw);
+
+       if (icr & REG_IS_MASK_IS_ASSERT) {
+               ret = IRQ_HANDLED;
+       } else
+               ret = IRQ_NONE;
+
+       return ret;
+}
+
+/*
  *  fjes_probe - Device Initialization Routine
  *
  *  Returns 0 on success, negative on failure
diff --git a/drivers/platform/x86/fjes/fjes_regs.h 
b/drivers/platform/x86/fjes/fjes_regs.h
index e7acf1f..1d4033f 100644
--- a/drivers/platform/x86/fjes/fjes_regs.h
+++ b/drivers/platform/x86/fjes/fjes_regs.h
@@ -52,8 +52,11 @@
 #define XSCT_RESPBAH        0x004C  /* Response Buffer Address High */
 
 /* Interrupt Control registers */
+#define XSCT_IS             0x0080  /* Interrupt status */
 #define XSCT_IMS            0x0084  /* Interrupt mas set */
 #define XSCT_IMC            0x0088  /* Interrupt mask clear */
+#define XSCT_IG             0x008C  /* Interrupt generator */
+#define XSCT_ICTL           0x0090  /* Interrupt control */
 
 
 /*
@@ -108,6 +111,15 @@ union REG_CS {
        __le32 Reg;
 };
 
+/* Interrupt Control registers */
+union REG_ICTL {
+       struct {
+               __le32 automak:1;
+               __le32 rsv0:31;
+       } Bits;
+       __le32 Reg;
+};
+
 enum REG_ICTL_MASK {
        REG_ICTL_MASK_INFO_UPDATE     = 1 << 20,
        REG_ICTL_MASK_DEV_STOP_REQ    = 1 << 19,
@@ -117,6 +129,10 @@ enum REG_ICTL_MASK {
        REG_ICTL_MASK_ALL             = GENMASK(20, 16),
 };
 
+enum REG_IS_MASK {
+       REG_IS_MASK_IS_ASSERT   = 1 << 31,
+       REG_IS_MASK_EPID                = GENMASK(15, 0),
+};
 
 struct fjes_hw;
 
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to