Initialize functions (init, reset ...) to control chip. Signed-off-by: Dong Yibo --- drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 35 +++ .../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c | 107 ++++++++ drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 15 +- .../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 28 ++ .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 255 ++++++++++++++++++ .../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 74 +++++ 6 files changed, 513 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h index ea28236669e3..527091e6a680 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h @@ -144,6 +144,25 @@ struct mucse_mbx_info { #include "rnpgbe_mbx.h" +struct lldp_status { + int enable; + int inteval; +}; + +struct mucse_hw_operations { + int (*init_hw)(struct mucse_hw *hw); + int (*reset_hw)(struct mucse_hw *hw); + void (*start_hw)(struct mucse_hw *hw); + /* ops to fw */ + void (*driver_status)(struct mucse_hw *hw, bool enable, int mode); +}; + +enum { + mucse_driver_insmod, + mucse_driver_suspuse, + mucse_driver_force_control_phy, +}; + struct mucse_hw { void *back; u8 pfvfnum; @@ -166,6 +185,7 @@ struct mucse_hw { int max_vfs; int max_vfs_noari; enum rnpgbe_hw_type hw_type; + struct mucse_hw_operations ops; struct mucse_dma_info dma; struct mucse_eth_info eth; struct mucse_mac_info mac; @@ -190,7 +210,11 @@ struct mucse_hw { #define M_HW_FEATURE_EEE BIT(17) #define M_HW_SOFT_MASK_OTHER_IRQ BIT(18) u32 feature_flags; + u32 driver_version; u16 usecstocount; + int nr_lane; + struct lldp_status lldp_status; + int link; }; struct mucse { @@ -225,5 +249,16 @@ struct rnpgbe_info { #define m_rd_reg(reg) readl(reg) #define m_wr_reg(reg, val) writel((val), reg) +#define hw_wr32(hw, reg, val) m_wr_reg((hw)->hw_addr + (reg), (val)) +#define dma_wr32(dma, reg, val) m_wr_reg((dma)->dma_base_addr + (reg), (val)) +#define dma_rd32(dma, reg) m_rd_reg((dma)->dma_base_addr + (reg)) +#define eth_wr32(eth, reg, val) m_wr_reg((eth)->eth_base_addr + (reg), (val)) +#define eth_rd32(eth, reg) m_rd_reg((eth)->eth_base_addr + (reg)) + +#define mucse_err(mucse, fmt, arg...) \ + dev_err(&(mucse)->pdev->dev, fmt, ##arg) + +#define mucse_dbg(mucse, fmt, arg...) \ + dev_dbg(&(mucse)->pdev->dev, fmt, ##arg) #endif /* _RNPGBE_H */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c index b0e5fda632f3..7a162b844fe4 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c @@ -7,6 +7,111 @@ #include "rnpgbe.h" #include "rnpgbe_hw.h" #include "rnpgbe_mbx.h" +#include "rnpgbe_mbx_fw.h" + +/** + * rnpgbe_init_hw_ops_n500 - Init hardware + * @hw: hw information structure + * + * rnpgbe_init_hw_ops_n500 first do a hw reset, then + * tries to start hw + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_init_hw_ops_n500(struct mucse_hw *hw) +{ + int status = 0; + /* Reset the hardware */ + status = hw->ops.reset_hw(hw); + if (status == 0) + hw->ops.start_hw(hw); + + return status; +} + +/** + * rnpgbe_reset_hw_ops_n500 - Do a hardware reset + * @hw: hw information structure + * + * rnpgbe_reset_hw_ops_n500 calls fw to do a hardware + * reset, and cleans some regs to default. + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_reset_hw_ops_n500(struct mucse_hw *hw) +{ + struct mucse_dma_info *dma = &hw->dma; + struct mucse_eth_info *eth = &hw->eth; + int err; + int i; + /* Call hw to stop dma */ + dma_wr32(dma, RNPGBE_DMA_AXI_EN, 0); + err = mucse_mbx_fw_reset_phy(hw); + if (err) + return err; + eth_wr32(eth, RNPGBE_ETH_ERR_MASK_VECTOR, + RNPGBE_PKT_LEN_ERR | RNPGBE_HDR_LEN_ERR); + dma_wr32(dma, RNPGBE_DMA_RX_PROG_FULL_THRESH, 0xa); + for (i = 0; i < 12; i++) + m_wr_reg(hw->ring_msix_base + RING_VECTOR(i), 0); + + hw->link = 0; + + return 0; +} + +/** + * rnpgbe_start_hw_ops_n500 - Setup hw to start + * @hw: hw information structure + * + * rnpgbe_start_hw_ops_n500 initializes default + * hw status, ready to start. + * + **/ +static void rnpgbe_start_hw_ops_n500(struct mucse_hw *hw) +{ + struct mucse_eth_info *eth = &hw->eth; + struct mucse_dma_info *dma = &hw->dma; + u32 value; + + value = dma_rd32(dma, RNPGBE_DMA_DUMY); + value |= BIT(0); + dma_wr32(dma, RNPGBE_DMA_DUMY, value); + dma_wr32(dma, RNPGBE_DMA_CONFIG, DMA_VEB_BYPASS); + dma_wr32(dma, RNPGBE_DMA_AXI_EN, (RX_AXI_RW_EN | TX_AXI_RW_EN)); + eth_wr32(eth, RNPGBE_ETH_BYPASS, 0); + eth_wr32(eth, RNPGBE_ETH_DEFAULT_RX_RING, 0); +} + +/** + * rnpgbe_driver_status_hw_ops_n500 - Echo driver status to hw + * @hw: hw information structure + * @enable: true or false status + * @mode: status mode + **/ +static void rnpgbe_driver_status_hw_ops_n500(struct mucse_hw *hw, + bool enable, + int mode) +{ + switch (mode) { + case mucse_driver_insmod: + mucse_mbx_ifinsmod(hw, enable); + break; + case mucse_driver_suspuse: + mucse_mbx_ifsuspuse(hw, enable); + break; + case mucse_driver_force_control_phy: + mucse_mbx_ifforce_control_mac(hw, enable); + break; + } +} + +static struct mucse_hw_operations hw_ops_n500 = { + .init_hw = &rnpgbe_init_hw_ops_n500, + .reset_hw = &rnpgbe_reset_hw_ops_n500, + .start_hw = &rnpgbe_start_hw_ops_n500, + .driver_status = &rnpgbe_driver_status_hw_ops_n500, +}; /** * rnpgbe_get_invariants_n500 - setup for hw info @@ -84,7 +189,9 @@ static void rnpgbe_get_invariants_n500(struct mucse_hw *hw) M_NET_FEATURE_STAG_OFFLOAD; /* start the default ahz, update later */ hw->usecstocount = 125; + hw->max_vfs_noari = 1; hw->max_vfs = 7; + memcpy(&hw->ops, &hw_ops_n500, sizeof(hw->ops)); } /** diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h index ff7bd9b21550..35e3cb77a38b 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h @@ -14,8 +14,21 @@ #define RNPGBE_RING_BASE (0x1000) #define RNPGBE_MAC_BASE (0x20000) #define RNPGBE_ETH_BASE (0x10000) - +/* dma regs */ +#define DMA_VEB_BYPASS BIT(4) +#define RNPGBE_DMA_CONFIG (0x0004) #define RNPGBE_DMA_DUMY (0x000c) +#define RNPGBE_DMA_AXI_EN (0x0010) +#define RX_AXI_RW_EN (0x03 << 0) +#define TX_AXI_RW_EN (0x03 << 2) +#define RNPGBE_DMA_RX_PROG_FULL_THRESH (0x00a0) +#define RING_VECTOR(n) (0x04 * (n)) +/* eth regs */ +#define RNPGBE_ETH_BYPASS (0x8000) +#define RNPGBE_ETH_ERR_MASK_VECTOR (0x8060) +#define RNPGBE_ETH_DEFAULT_RX_RING (0x806c) +#define RNPGBE_PKT_LEN_ERR (2) +#define RNPGBE_HDR_LEN_ERR (1) /* chip resourse */ #define RNPGBE_MAX_QUEUES (8) /* multicast control table */ diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c index 61dd0d232d99..ba21e3858c0e 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -38,6 +38,17 @@ static struct pci_device_id rnpgbe_pci_tbl[] = { {0, }, }; +/** + * rnpgbe_sw_init - Init driver private status + * @mucse: pointer to private structure + * + * @return: 0 on success, negative on failure + **/ +static int rnpgbe_sw_init(struct mucse *mucse) +{ + return 0; +} + /** * rnpgbe_add_adapter - add netdev for this pci_dev * @pdev: PCI device information structure @@ -127,8 +138,12 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, } hw->hw_addr = hw_addr; hw->dma.dma_version = dma_version; + hw->driver_version = 0x0002040f; + hw->nr_lane = 0; ii->get_invariants(hw); hw->mbx.ops.init_params(hw); + /* echo fw driver insmod */ + hw->ops.driver_status(hw, true, mucse_driver_insmod); err = mucse_mbx_get_capability(hw); if (err) { @@ -137,6 +152,16 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev, goto err_free_net; } + err = rnpgbe_sw_init(mucse); + if (err) + goto err_free_net; + + err = hw->ops.reset_hw(hw); + if (err) { + dev_err(&pdev->dev, "Hw reset failed\n"); + goto err_free_net; + } + return 0; err_free_net: @@ -202,11 +227,14 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id) **/ static void rnpgbe_rm_adapter(struct mucse *mucse) { + struct mucse_hw *hw = &mucse->hw; struct net_device *netdev; rnpgbe_devlink_unregister(mucse); netdev = mucse->netdev; + hw->ops.driver_status(hw, false, mucse_driver_insmod); free_netdev(netdev); + mucse->netdev = NULL; } /** diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 1674229fcd43..18f57ef8b1ad 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -3,6 +3,7 @@ #include +#include "rnpgbe.h" #include "rnpgbe_mbx_fw.h" /** @@ -138,3 +139,257 @@ int mucse_mbx_get_capability(struct mucse_hw *hw) return err; } + +/** + * mbx_req_cookie - Alloc a cookie structure + * @priv_len: private length for this cookie + * + * @return: cookie structure on success + **/ +static struct mbx_req_cookie *mbx_cookie_zalloc(int priv_len) +{ + struct mbx_req_cookie *cookie; + + cookie = kzalloc(struct_size(cookie, priv, priv_len), GFP_KERNEL); + + if (cookie) { + cookie->timeout_jiffes = 30 * HZ; + cookie->magic = COOKIE_MAGIC; + cookie->priv_len = priv_len; + } + + return cookie; +} + +/** + * mucse_mbx_fw_post_req - Posts a mbx req to firmware and wait reply + * @hw: Pointer to the HW structure + * @req: Pointer to the cmd req structure + * @cookie: Pointer to the req cookie + * + * mucse_mbx_fw_post_req posts a mbx req to firmware and wait for the + * reply. cookie->wait will be set in irq handler. + * + * @return: 0 on success, negative on failure + **/ +static int mucse_mbx_fw_post_req(struct mucse_hw *hw, + struct mbx_fw_cmd_req *req, + struct mbx_req_cookie *cookie) +{ + int len = le32_to_cpu(req->datalen) + MBX_REQ_HDR_LEN; + int err = 0; + + cookie->errcode = 0; + cookie->done = 0; + init_waitqueue_head(&cookie->wait); + + err = mutex_lock_interruptible(&hw->mbx.lock); + if (err) + return err; + + err = mucse_write_mbx(hw, (u32 *)req, + L_WD(len), + MBX_FW); + if (err) { + mutex_unlock(&hw->mbx.lock); + return err; + } + + if (cookie->timeout_jiffes != 0) { +retry: + err = wait_event_interruptible_timeout(cookie->wait, + cookie->done == 1, + cookie->timeout_jiffes); + if (err == -ERESTARTSYS) + goto retry; + if (err == 0) + err = -ETIME; + else + err = 0; + } else { +retry_no_timeout: + err = wait_event_interruptible(cookie->wait, cookie->done == 1); + if (err == -ERESTARTSYS) + goto retry_no_timeout; + } + + mutex_unlock(&hw->mbx.lock); + + if (cookie->errcode) + err = cookie->errcode; + + return err; +} + +/** + * rnpgbe_mbx_lldp_get - Get lldp status from hw + * @hw: Pointer to the HW structure + * + * @return: 0 on success, negative on failure + **/ +int rnpgbe_mbx_lldp_get(struct mucse_hw *hw) +{ + struct mbx_req_cookie *cookie = NULL; + struct get_lldp_reply *get_lldp; + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int err; + + cookie = mbx_cookie_zalloc(sizeof(*get_lldp)); + if (!cookie) + return -ENOMEM; + + get_lldp = (struct get_lldp_reply *)cookie->priv; + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + build_get_lldp_req(&req, cookie, hw->nr_lane); + if (hw->mbx.irq_enabled) { + err = mucse_mbx_fw_post_req(hw, &req, cookie); + } else { + err = mucse_fw_send_cmd_wait(hw, &req, &reply); + get_lldp = &reply.get_lldp; + } + + if (err == 0) { + hw->lldp_status.enable = le32_to_cpu(get_lldp->value); + hw->lldp_status.inteval = le32_to_cpu(get_lldp->inteval); + } + + kfree(cookie); + + return err; +} + +/** + * mucse_mbx_ifinsmod - Echo driver insmod status to hw + * @hw: Pointer to the HW structure + * @status: true for insmod, false for rmmod + * + * @return: 0 on success, negative on failure + **/ +int mucse_mbx_ifinsmod(struct mucse_hw *hw, int status) +{ + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int len; + int err; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + build_ifinsmod(&req, hw->driver_version, status); + len = le32_to_cpu(req.datalen) + MBX_REQ_HDR_LEN; + err = mutex_lock_interruptible(&hw->mbx.lock); + if (err) + return err; + + if (status) { + err = hw->mbx.ops.write_posted(hw, (u32 *)&req, + L_WD(len), + MBX_FW); + } else { + err = hw->mbx.ops.write(hw, (u32 *)&req, + L_WD(len), + MBX_FW); + } + + mutex_unlock(&hw->mbx.lock); + return err; +} + +/** + * mucse_mbx_ifsuspuse - Echo driver suspuse status to hw + * @hw: Pointer to the HW structure + * @status: true for suspuse, false for no susupuse + * + * mucse_mbx_ifsuspuse echo driver susupus status to hw. The + * status is used to enter wol status for hw. + * + * @return: 0 on success, negative on failure + **/ +int mucse_mbx_ifsuspuse(struct mucse_hw *hw, int status) +{ + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int len; + int err; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + build_ifsuspuse(&req, hw->nr_lane, status); + len = le32_to_cpu(req.datalen) + MBX_REQ_HDR_LEN; + err = mutex_lock_interruptible(&hw->mbx.lock); + if (err) + return err; + + err = hw->mbx.ops.write_posted(hw, (u32 *)&req, + L_WD(len), + MBX_FW); + mutex_unlock(&hw->mbx.lock); + + return err; +} + +/** + * mucse_mbx_ifforce_control_mac - Echo force mac control to hw + * @hw: Pointer to the HW structure + * @status: true for force control, false for not + * + * @return: 0 on success, negative on failure + **/ +int mucse_mbx_ifforce_control_mac(struct mucse_hw *hw, int status) +{ + struct mbx_fw_cmd_reply reply; + struct mbx_fw_cmd_req req; + int len; + int err; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + build_ifforce(&req, hw->nr_lane, status); + len = le32_to_cpu(req.datalen) + MBX_REQ_HDR_LEN; + err = mutex_lock_interruptible(&hw->mbx.lock); + if (err) + return err; + + err = hw->mbx.ops.write_posted(hw, (u32 *)&req, + L_WD(len), + MBX_FW); + mutex_unlock(&hw->mbx.lock); + + return err; +} + +/** + * mucse_mbx_fw_reset_phy - Posts a mbx req to reset hw + * @hw: Pointer to the HW structure + * + * mucse_mbx_fw_reset_phy posts a mbx req to firmware to reset hw. + * It uses mucse_fw_send_cmd_wait if no irq, and mucse_mbx_fw_post_req + * if other irq is registered. + * + * @return: 0 on success, negative on failure + **/ +int mucse_mbx_fw_reset_phy(struct mucse_hw *hw) +{ + struct mbx_fw_cmd_req req; + struct mbx_fw_cmd_reply reply; + int ret; + + memset(&req, 0, sizeof(req)); + memset(&reply, 0, sizeof(reply)); + if (hw->mbx.irq_enabled) { + struct mbx_req_cookie *cookie = mbx_cookie_zalloc(0); + + if (!cookie) + return -ENOMEM; + + build_reset_phy_req(&req, cookie); + ret = mucse_mbx_fw_post_req(hw, &req, cookie); + kfree(cookie); + return ret; + + } else { + build_reset_phy_req(&req, &req); + return mucse_fw_send_cmd_wait(hw, &req, &reply); + } +} diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h index a24c5d4e0075..9e07858f2733 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h @@ -563,6 +563,80 @@ static inline void build_phy_abalities_req(struct mbx_fw_cmd_req *req, req->cookie = cookie; } +static inline void build_get_lldp_req(struct mbx_fw_cmd_req *req, void *cookie, + int nr_lane) +{ +#define LLDP_TX_GET (1) + + req->flags = 0; + req->opcode = cpu_to_le32(LLDP_TX_CTRL); + req->datalen = cpu_to_le32(sizeof(req->lldp_tx)); + req->cookie = cookie; + req->reply_lo = 0; + req->reply_hi = 0; + req->lldp_tx.lane = cpu_to_le32(nr_lane); + req->lldp_tx.op = cpu_to_le32(LLDP_TX_GET); + req->lldp_tx.enable = 0; +} + +static inline void build_ifinsmod(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, + int status) +{ + req->flags = 0; + req->opcode = cpu_to_le32(DRIVER_INSMOD); + req->datalen = cpu_to_le32(sizeof(req->ifinsmod)); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifinsmod.lane = cpu_to_le32(nr_lane); + req->ifinsmod.status = cpu_to_le32(status); +} + +static inline void build_ifsuspuse(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, + int status) +{ + req->flags = 0; + req->opcode = cpu_to_le32(SYSTEM_SUSPUSE); + req->datalen = cpu_to_le32(sizeof(req->ifsuspuse)); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifinsmod.lane = cpu_to_le32(nr_lane); + req->ifinsmod.status = cpu_to_le32(status); +} + +static inline void build_ifforce(struct mbx_fw_cmd_req *req, + unsigned int nr_lane, + int status) +{ + req->flags = 0; + req->opcode = cpu_to_le32(SYSTEM_FORCE); + req->datalen = cpu_to_le32(sizeof(req->ifforce)); + req->cookie = NULL; + req->reply_lo = 0; + req->reply_hi = 0; + req->ifforce.lane = cpu_to_le32(nr_lane); + req->ifforce.status = cpu_to_le32(status); +} + +static inline void build_reset_phy_req(struct mbx_fw_cmd_req *req, + void *cookie) +{ + req->flags = 0; + req->opcode = cpu_to_le32(RESET_PHY); + req->datalen = 0; + req->reply_lo = 0; + req->reply_hi = 0; + req->cookie = cookie; +} + int mucse_mbx_get_capability(struct mucse_hw *hw); +int rnpgbe_mbx_lldp_get(struct mucse_hw *hw); +int mucse_mbx_ifinsmod(struct mucse_hw *hw, int status); +int mucse_mbx_ifsuspuse(struct mucse_hw *hw, int status); +int mucse_mbx_ifforce_control_mac(struct mucse_hw *hw, int status); +int mucse_mbx_fw_reset_phy(struct mucse_hw *hw); #endif /* _RNPGBE_MBX_FW_H */ -- 2.25.1